diff --git a/.firebaserc b/.firebaserc new file mode 100644 index 0000000..624417b --- /dev/null +++ b/.firebaserc @@ -0,0 +1,18 @@ +{ + "projects": { + "default": "benefit-decision-toolkit-play" + }, + "targets": { + "benefit-decision-toolkit-play": { + "hosting": { + "screener-frontend": [ + "bdt-screener" + ], + "builder-frontend": [ + "bdt-builder" + ] + } + } + }, + "etags": {} +} \ No newline at end of file diff --git a/.firebaserc.old.json b/.firebaserc.old.json new file mode 100644 index 0000000..0268c30 --- /dev/null +++ b/.firebaserc.old.json @@ -0,0 +1,18 @@ +{ + "projects": { + "default": "benefits-decision-toolkit" + }, + "targets": { + "benefits-decision-toolkit": { + "hosting": { + "screener-frontend": [ + "benefits-decision-toolkit" + ], + "builder-frontend": [ + "bdt-screener-builder" + ] + } + } + }, + "etags": {} +} \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..1593213 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @Michael-Dratch \ No newline at end of file diff --git a/.gitignore b/.gitignore index e397f07..99abda2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,74 @@ -target/ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +firebase-debug.log* +firebase-debug.*.log* -thunder-tests/thunderResponses.json -thunder-tests/res-files/ -thunder-tests/thunderActivity.json +# Firebase cache +.firebase/ + +# Firebase config + +# Uncomment this if you'd like others to create their own Firebase project. +# For a team working on the same Firebase project(s), it is recommended to leave +# it commented so all members can deploy to the same project(s) in .firebaserc. +# .firebaserc + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# dataconnect generated files +.dataconnect +# IntelliJ +.idea +*.ipr +*.iml +*.iws diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..a0ccf77 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Environment-dependent path to Maven home directory +/mavenHomeManager.xml diff --git a/.idea/Benefits-Decision-Toolkit.iml b/.idea/Benefits-Decision-Toolkit.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/Benefits-Decision-Toolkit.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..6f29fee --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..0310d44 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idx/dev.nix b/.idx/dev.nix index ecd9080..9e23eb7 100644 --- a/.idx/dev.nix +++ b/.idx/dev.nix @@ -1,5 +1,5 @@ # To learn more about how to use Nix to configure your environment -# see: https://developers.google.com/idx/guides/customize-idx-env +# see: https://firebase.google.com/docs/studio/customize-workspace { pkgs, ... }: { # Which nixpkgs channel to use. channel = "stable-24.05"; # or "unstable" @@ -8,36 +8,21 @@ packages = [ pkgs.maven pkgs.quarkus - pkgs.jdk17 + pkgs.jdk21 ]; # Sets environment variables in the workspace - env = { - # PORT = "8080"; - }; + env = {}; idx = { # Search for the extensions you want on https://open-vsx.org/ and use "publisher.id" extensions = [ - "jellydn.toggle-excluded-files" - "waderyan.gitblame" - "kie-group.dmn-vscode-extension" - "rangav.vscode-thunder-client" - "redhat.vscode-xml" - "mhutchie.git-graph" - "redhat.java" - "redhat.vscode-quarkus" + # "vscodevim.vim" ]; # Enable previews - # previews = { - # enable = true; - # previews = { - # web = { - # command = [ - # "bin/dev" - # ]; - # manager = "web"; - # }; + previews = { + enable = true; + previews = { # web = { # # Example: run "npm run dev" with PORT set to IDX's defined port for previews, # # and show it in IDX's web preview panel @@ -48,19 +33,20 @@ # PORT = "$PORT"; # }; # }; - # }; - # }; + }; + }; # Workspace lifecycle hooks workspace = { # Runs when a workspace is first created onCreate = { - install-default-settings = "cp .idx/default-vscode-settings.jsonc /home/user/.codeoss-cloudworkstations/data/Machine/settings.json"; - install-thunder-client-tests = "ln -s /home/user/dmn-benefit-toolbox/thunder-tests/ /home/user/.codeoss-cloudworkstations/data/User/globalStorage/rangav.vscode-thunder-client"; + # Example: install JS dependencies from NPM + # npm-install = "npm install"; }; # Runs when the workspace is (re)started onStart = { - start-quarkus-dev-server = "bin/dev"; + # Example: start a background task to watch and re-build backend code + # watch-backend = "npm run watch-backend"; }; }; }; diff --git a/.idx/integrations.json b/.idx/integrations.json index 19d81fc..d3680d7 100644 --- a/.idx/integrations.json +++ b/.idx/integrations.json @@ -1,4 +1,7 @@ { + "firebase_hosting": { + "deploymentHostingUrl": "https://benefit-decision-toolkit-play.web.app" + }, "cloud_run_deploy": { "region": "us-east4", "sourceFlag": "--source .", diff --git a/README.md b/README.md index f5e9f62..5d2c834 100644 --- a/README.md +++ b/README.md @@ -1,119 +1,3 @@ -# Benefit Decision Toolkit +### Benefits Decision Toolkit Web App -**Use [DMN](https://www.omg.org/dmn/) and [FEEL](https://docs.camunda.io/docs/components/modeler/feel/what-is-feel/) to create APIs and Screeners for public benefit rules.** - -## Motivation - -Why hire a team of software engineers to codify rules that your benefit experts already know inside and out? - -Why design a screening tool from scratch when your goal is simply to deploy a functionally accurate service as quickly as possible? - -***Benefit Decision Toolkit simplifies the management of eligibility rules and screeners so motivated subject matter experts can create useful tools with less hand-holding from traditional software teams.*** - -## Examples - -### The Philly Property Tax Relief Screener - -As a proof of concept, we've built a screener for several of the [tax relief benefits available in Philadelphia](https://www.phila.gov/services/payments-assistance-taxes/taxes/property-and-real-estate-taxes/get-real-estate-tax-relief/). - -You can interact with the screener yourself at: https://phillypropertytaxrelief.org. - -### JSON ("REST") API - -A JSON API is generated from the eligibility rules created in DMN/FEEL. You can interact with the endpoints of this API at: https://phillypropertytaxrelief.org/q/swagger-ui/ - -## Setup a Development Environment - -Since the ultimate goal of this project is to enable a development environment that is accessible to those without a software engineering background, we are intrigued by tools like [Firebase Studio](https://firebase.google.com/docs/studio) as a precursor/mockup for a more specialized service that we'd like to build (eventually). - -With Firebase Studio, the environment is in the cloud, and can be accessed from any computer without any manual setup. - -If you're an experienced engineer, however, you may want to skip Firebase Studio in favor of your own preferred setup. (Up to you.) - -### Use Firebase Studio (simplest) - -The easiest way to get a feel for Benefit Decision Toolkit is to [fork this repo*](https://github.com/CodeForPhilly/benefit-decision-toolkit/fork) and then open it (from the fork!) in Firebase Studio: - - - - - - Open in Firebase Studio - - - -When you open Benefit Decision Toolkit in Firebase Studio, a development machine will be created and configured for you in the cloud. (This will take a few minutes.) - -After the first time you open the project, you can just return to https://studio.firebase.google.com to pick up where you left off. - -_*Note on forking: you can skip this step if you are just curious or otherwise don't intend to contribute changes back to the project._ - -### Use Devbox (medium simple) - -The second easiest way to get started is to [fork this repo*](https://github.com/CodeForPhilly/benefit-decision-toolkit/fork), clone it to your local machine, and use the included Devbox configuration: - -[![Built with Devbox](https://www.jetify.com/img/devbox/shield_galaxy.svg)](https://www.jetify.com/devbox/docs/contributor-quickstart/) - -[Devbox](https://www.jetify.com/devbox) is a way to create isolated development environments on your own machine. - -We're experimenting w/ Devbox for the same reason we're experimenting w/ Firebase Studio; we're interested in how this approach to managing development environments could form the basis of some future specialized/packaged toolkit. - -### Roll your own (for experienced developers) - -Of course, you can also manually setup a laptop/desktop to work on Benefit Decision Toolkit, but it isn't recommended unless you really know what you're doing. - -### Additional setup notes - -#### Forms development - -If you're planning to develop forms (the basis of eligibility screeners) in addition to eligibility rules, then you'll also need to download [Camunda Modeler](https://camunda.com/download/modeler/). - -#### Kicking the tires and testing your work - -You can run `bin/dev`* from a terminal to start the Quarkus development server. This will serve the API and any existing eligibility screeners from `https://localhost:8080`**. - -*_If you are using Firebase Studio the Quarkus dev server should automatically start when you load the workspace._ - -**_Since Firebase Studio is a browser-based environment, the system will map the URL for the dev server to an address you can reach from a browser in your local machine. You can access this address by clicking on the Firebase Studio logo in the left sidebar, then clicking into the "Backend Ports" panel._ - -Screenshot of Backend Ports list in Firebase Studio - -## Technologies overview - -We use a combination of open-source tools ([Kogito](https://kogito.kie.org/) and [form-js](https://bpmn.io/toolkit/form-js/)) with some scaffolding to tie them together and make them easier to use. - -Here are some high-level things to orient you... - -### DMN Files - -DMN files (`.dmn`) can be edited directly in VS Code via the [DMN Editor extension](https://marketplace.visualstudio.com/items?itemName=kie-group.dmn-vscode-extension). If you're using Firebase Studio as described above, then this extension is already installed for you. If you're using another dev environment, then you'll need to install the extension manually. - -You may occasionally need to interact with the raw XML text of the DMN; this can be done via the "Reopen with Text Editor" feature of VS Code. - -A good orientation on the basics of DMN can be found [here](https://learn-dmn-in-15-minutes.com/). - -In this project, DMN (and its accompanying expression language FEEL) acts as the "source code" for a JSON web API. Kogito generates this API (with Java) when you run the Quarkus development server (automatic with Firebase Studio, or by running the `bin/dev` script). - -### Form Files - -Form files (`.form`) can be edited using [Camunda Modeler](https://camunda.com/download/modeler/). The modeler provides a UI for designing the form's layout and logic (which often incorporates FEEL expressions). - -Behind the scenes,the form is saved in JSON format. - -When it comes time to use the form in a screener, form-js interprets this JSON and uses it to display the form on a web page. - -### Eligibility Screeners - -To create the [Philadelphia Property Tax Relief Screener](https://phillypropertytaxrelief.org), we've written a [Qute template](https://quarkus.io/guides/qute) which displays the form, posts data as it is collected to the eligibility API (the one that is built from the DMN), and receives back eligibility results for display on the form. - -For future screeners, we envision packaging up this functionality somehow, allowing the entire screening and results process to be included as part of other websites and tools, the content of which is outside the scope of this project. - -## License -See the [LICENSE](https://github.com/CodeForPhilly/benefit-decision-toolkit/blob/main/LICENSE.md) file for license rights and limitations (MIT). +Welcome to the BDT Web App! The BDT Web App is a project focusing on building a cloud-based platform that makes it easy to build, test, and share benefit eligibility screeners. \ No newline at end of file diff --git a/builder-api/.dockerignore b/builder-api/.dockerignore new file mode 100644 index 0000000..94810d0 --- /dev/null +++ b/builder-api/.dockerignore @@ -0,0 +1,5 @@ +* +!target/*-runner +!target/*-runner.jar +!target/lib/* +!target/quarkus-app/* \ No newline at end of file diff --git a/builder-api/.gitignore b/builder-api/.gitignore new file mode 100644 index 0000000..18ad5f5 --- /dev/null +++ b/builder-api/.gitignore @@ -0,0 +1,47 @@ +#Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +release.properties +.flattened-pom.xml + +# Eclipse +.project +.classpath +.settings/ +bin/ + +# IntelliJ +.idea +*.ipr +*.iml +*.iws + +# NetBeans +nb-configuration.xml + +# Visual Studio Code +.vscode +.factorypath + +# OSX +.DS_Store + +# Vim +*.swp +*.swo + +# patch +*.orig +*.rej + +# Local environment +.env + +# Plugin directory +/.quarkus/cli/plugins/ +# TLS Certificates +.certs/ + +config/ \ No newline at end of file diff --git a/builder-api/.mvn/wrapper/.gitignore b/builder-api/.mvn/wrapper/.gitignore new file mode 100644 index 0000000..e72f5e8 --- /dev/null +++ b/builder-api/.mvn/wrapper/.gitignore @@ -0,0 +1 @@ +maven-wrapper.jar diff --git a/builder-api/.mvn/wrapper/MavenWrapperDownloader.java b/builder-api/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..fe7d037 --- /dev/null +++ b/builder-api/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.net.URI; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.concurrent.ThreadLocalRandom; + +public final class MavenWrapperDownloader { + private static final String WRAPPER_VERSION = "3.3.2"; + + private static final boolean VERBOSE = Boolean.parseBoolean(System.getenv("MVNW_VERBOSE")); + + public static void main(String[] args) { + log("Apache Maven Wrapper Downloader " + WRAPPER_VERSION); + + if (args.length != 2) { + System.err.println(" - ERROR wrapperUrl or wrapperJarPath parameter missing"); + System.exit(1); + } + + try { + log(" - Downloader started"); + final URL wrapperUrl = URI.create(args[0]).toURL(); + final String jarPath = args[1].replace("..", ""); // Sanitize path + final Path wrapperJarPath = Paths.get(jarPath).toAbsolutePath().normalize(); + downloadFileFromURL(wrapperUrl, wrapperJarPath); + log("Done"); + } catch (IOException e) { + System.err.println("- Error downloading: " + e.getMessage()); + if (VERBOSE) { + e.printStackTrace(); + } + System.exit(1); + } + } + + private static void downloadFileFromURL(URL wrapperUrl, Path wrapperJarPath) + throws IOException { + log(" - Downloading to: " + wrapperJarPath); + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + final String username = System.getenv("MVNW_USERNAME"); + final char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + Path temp = wrapperJarPath + .getParent() + .resolve(wrapperJarPath.getFileName() + "." + + Long.toUnsignedString(ThreadLocalRandom.current().nextLong()) + ".tmp"); + try (InputStream inStream = wrapperUrl.openStream()) { + Files.copy(inStream, temp, StandardCopyOption.REPLACE_EXISTING); + Files.move(temp, wrapperJarPath, StandardCopyOption.REPLACE_EXISTING); + } finally { + Files.deleteIfExists(temp); + } + log(" - Downloader complete"); + } + + private static void log(String msg) { + if (VERBOSE) { + System.out.println(msg); + } + } + +} diff --git a/builder-api/.mvn/wrapper/maven-wrapper.properties b/builder-api/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..1a580be --- /dev/null +++ b/builder-api/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +wrapperVersion=3.3.2 +distributionType=source +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar \ No newline at end of file diff --git a/builder-api/README.md b/builder-api/README.md new file mode 100644 index 0000000..00e567d --- /dev/null +++ b/builder-api/README.md @@ -0,0 +1,67 @@ +# builder-api + +This project uses Quarkus, the Supersonic Subatomic Java Framework. + +If you want to learn more about Quarkus, please visit its website: . + +## Running the application in dev mode + +You can run your application in dev mode that enables live coding using: + +```shell script +./mvnw quarkus:dev +``` + +> **_NOTE:_** Quarkus now ships with a Dev UI, which is available in dev mode only at . + +## Packaging and running the application + +The application can be packaged using: + +```shell script +./mvnw package +``` + +It produces the `quarkus-run.jar` file in the `target/quarkus-app/` directory. +Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/quarkus-app/lib/` directory. + +The application is now runnable using `java -jar target/quarkus-app/quarkus-run.jar`. + +If you want to build an _über-jar_, execute the following command: + +```shell script +./mvnw package -Dquarkus.package.jar.type=uber-jar +``` + +The application, packaged as an _über-jar_, is now runnable using `java -jar target/*-runner.jar`. + +## Creating a native executable + +You can create a native executable using: + +```shell script +./mvnw package -Dnative +``` + +Or, if you don't have GraalVM installed, you can run the native executable build in a container using: + +```shell script +./mvnw package -Dnative -Dquarkus.native.container-build=true +``` + +You can then execute your native executable with: `./target/builder-api-1.0.0-SNAPSHOT-runner` + +If you want to learn more about building native executables, please consult . + +## Related Guides + +- REST ([guide](https://quarkus.io/guides/rest)): A Jakarta REST implementation utilizing build time processing and Vert.x. This extension is not compatible with the quarkus-resteasy extension, or any of the extensions that depend on it. +- REST Jackson ([guide](https://quarkus.io/guides/rest#json-serialisation)): Jackson serialization support for Quarkus REST. This extension is not compatible with the quarkus-resteasy extension, or any of the extensions that depend on it + +## Provided Code + +### REST + +Easily start your REST Web Services + +[Related guide section...](https://quarkus.io/guides/getting-started-reactive#reactive-jax-rs-resources) diff --git a/builder-api/mvnw b/builder-api/mvnw new file mode 100755 index 0000000..5e9618c --- /dev/null +++ b/builder-api/mvnw @@ -0,0 +1,332 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.2 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ]; then + + if [ -f /usr/local/etc/mavenrc ]; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ]; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ]; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false +darwin=false +mingw=false +case "$(uname)" in +CYGWIN*) cygwin=true ;; +MINGW*) mingw=true ;; +Darwin*) + darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="$(/usr/libexec/java_home)" + export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home" + export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ]; then + if [ -r /etc/gentoo-release ]; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin; then + [ -n "$JAVA_HOME" ] \ + && JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] \ + && CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw; then + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] \ + && JAVA_HOME="$( + cd "$JAVA_HOME" || ( + echo "cannot cd into $JAVA_HOME." >&2 + exit 1 + ) + pwd + )" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "$javaExecutable" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then + if $darwin; then + javaHome="$(dirname "$javaExecutable")" + javaExecutable="$(cd "$javaHome" && pwd -P)/javac" + else + javaExecutable="$(readlink -f "$javaExecutable")" + fi + javaHome="$(dirname "$javaExecutable")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ]; then + 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 + else + JAVACMD="$( + \unset -f command 2>/dev/null + \command -v java + )" + fi +fi + +if [ ! -x "$JAVACMD" ]; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ]; then + echo "Warning: JAVA_HOME environment variable is not set." >&2 +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ]; then + echo "Path not specified to find_maven_basedir" >&2 + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ]; do + if [ -d "$wdir"/.mvn ]; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$( + cd "$wdir/.." || exit 1 + pwd + ) + fi + # end of workaround + done + printf '%s' "$( + cd "$basedir" || exit 1 + pwd + )" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' <"$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") +if [ -z "$BASE_DIR" ]; then + exit 1 +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" +else + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in wrapperUrl) + wrapperUrl="$safeValue" + break + ;; + esac + done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget >/dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl >/dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in wrapperSha256Sum) + wrapperSha256Sum=$value + break + ;; + esac +done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum >/dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c >/dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c >/dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi +fi + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] \ + && JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] \ + && CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] \ + && MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# shellcheck disable=SC2086 # safe args +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/builder-api/mvnw.cmd b/builder-api/mvnw.cmd new file mode 100644 index 0000000..4136715 --- /dev/null +++ b/builder-api/mvnw.cmd @@ -0,0 +1,206 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.2 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. >&2 +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. >&2 +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. >&2 +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. >&2 +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash;"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Error 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Error 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Error 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/builder-api/pom.xml b/builder-api/pom.xml new file mode 100644 index 0000000..177da50 --- /dev/null +++ b/builder-api/pom.xml @@ -0,0 +1,172 @@ + + + 4.0.0 + org.acme + builder-api + 1.0.0-SNAPSHOT + + + 3.14.0 + 21 + UTF-8 + UTF-8 + quarkus-bom + io.quarkus.platform + 3.23.0 + true + 3.5.2 + + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + + + + + io.quarkus + quarkus-rest + + + io.quarkus + quarkus-rest-jackson + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + com.google.firebase + firebase-admin + 9.4.3 + + + com.fasterxml.jackson.core + jackson-databind + 2.19.0 + + + org.kie + kie-dmn-core + 10.0.0 + + + org.drools + drools-xml-support + 10.0.0 + + + org.mockito + mockito-junit-jupiter + 5.18.0 + test + + + + + + org.xerial + sqlite-jdbc + 3.40.1.0 + + + io.quarkiverse.jdbc + quarkus-jdbc-sqlite + 3.0.0 + + + io.quarkus + quarkus-agroal + + + + + + + + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus.platform.version} + true + + + + build + generate-code + generate-code-tests + native-image-agent + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + true + + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native + + + native + + + + false + true + + + + diff --git a/builder-api/src/main/docker/Dockerfile.jvm b/builder-api/src/main/docker/Dockerfile.jvm new file mode 100644 index 0000000..aece313 --- /dev/null +++ b/builder-api/src/main/docker/Dockerfile.jvm @@ -0,0 +1,103 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# +# Before building the container image run: +# +# ./mvnw package -DskipTests +# +# Then, build the image with: +# +# podman build -f src/main/docker/Dockerfile.jvm -t us-central1-docker.pkg.dev/benefits-decision-toolkit/benefit-decision-toolkit/builder-api:latest . +# +# push to google artifact registry +# +# podman push us-central1-docker.pkg.dev/benefits-decision-toolkit/benefit-decision-toolkit/builder-api:latest +# +# run the container using: +# +# docker run -i --rm -p 8080:8080 us-central1-docker.pkg.dev/benefits-decision-toolkit/benefit-decision-toolkit/builder-api:latest +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. +# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 +# when running the container +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 quarkus/builder-api-jvm +# +# This image uses the `run-java.sh` script to run the application. +# This scripts computes the command line to execute your Java application, and +# includes memory/GC tuning. +# You can configure the behavior using the following environment properties: +# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") - Be aware that this will override +# the default JVM options, use `JAVA_OPTS_APPEND` to append options +# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options +# in JAVA_OPTS (example: "-Dsome.property=foo") +# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is +# used to calculate a default maximal heap memory based on a containers restriction. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio +# of the container available memory as set here. The default is `50` which means 50% +# of the available memory is used as an upper boundary. You can skip this mechanism by +# setting this value to `0` in which case no `-Xmx` option is added. +# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This +# is used to calculate a default initial heap memory based on the maximum heap memory. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio +# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` +# is used as the initial heap size. You can skip this mechanism by setting this value +# to `0` in which case no `-Xms` option is added (example: "25") +# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. +# This is used to calculate the maximum value of the initial heap memory. If used in +# a container without any memory constraints for the container then this option has +# no effect. If there is a memory constraint then `-Xms` is limited to the value set +# here. The default is 4096MB which means the calculated value of `-Xms` never will +# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") +# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output +# when things are happening. This option, if set to true, will set +# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). +# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: +# true"). +# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). +# - CONTAINER_CORE_LIMIT: A calculated core limit as described in +# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") +# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). +# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. +# (example: "20") +# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. +# (example: "40") +# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. +# (example: "4") +# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus +# previous GC times. (example: "90") +# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") +# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") +# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should +# contain the necessary JRE command-line options to specify the required GC, which +# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). +# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") +# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") +# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be +# accessed directly. (example: "foo.example.com,bar.example.com") +# +### +FROM registry.access.redhat.com/ubi9/openjdk-21:1.21 + +ENV LANGUAGE='en_US:en' + + +# We make four distinct layers so if there are application changes the library layers can be re-used +COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/ +COPY --chown=185 target/quarkus-app/*.jar /deployments/ +COPY --chown=185 target/quarkus-app/app/ /deployments/app/ +COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/ + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] + diff --git a/builder-api/src/main/docker/Dockerfile.legacy-jar b/builder-api/src/main/docker/Dockerfile.legacy-jar new file mode 100644 index 0000000..4e8cffc --- /dev/null +++ b/builder-api/src/main/docker/Dockerfile.legacy-jar @@ -0,0 +1,94 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package -Dquarkus.package.jar.type=legacy-jar +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/builder-api-legacy-jar . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/builder-api-legacy-jar +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. +# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 +# when running the container +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 quarkus/builder-api-legacy-jar +# +# This image uses the `run-java.sh` script to run the application. +# This scripts computes the command line to execute your Java application, and +# includes memory/GC tuning. +# You can configure the behavior using the following environment properties: +# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") - Be aware that this will override +# the default JVM options, use `JAVA_OPTS_APPEND` to append options +# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options +# in JAVA_OPTS (example: "-Dsome.property=foo") +# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is +# used to calculate a default maximal heap memory based on a containers restriction. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio +# of the container available memory as set here. The default is `50` which means 50% +# of the available memory is used as an upper boundary. You can skip this mechanism by +# setting this value to `0` in which case no `-Xmx` option is added. +# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This +# is used to calculate a default initial heap memory based on the maximum heap memory. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio +# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` +# is used as the initial heap size. You can skip this mechanism by setting this value +# to `0` in which case no `-Xms` option is added (example: "25") +# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. +# This is used to calculate the maximum value of the initial heap memory. If used in +# a container without any memory constraints for the container then this option has +# no effect. If there is a memory constraint then `-Xms` is limited to the value set +# here. The default is 4096MB which means the calculated value of `-Xms` never will +# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") +# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output +# when things are happening. This option, if set to true, will set +# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). +# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: +# true"). +# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). +# - CONTAINER_CORE_LIMIT: A calculated core limit as described in +# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") +# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). +# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. +# (example: "20") +# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. +# (example: "40") +# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. +# (example: "4") +# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus +# previous GC times. (example: "90") +# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") +# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") +# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should +# contain the necessary JRE command-line options to specify the required GC, which +# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). +# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") +# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") +# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be +# accessed directly. (example: "foo.example.com,bar.example.com") +# +### +FROM registry.access.redhat.com/ubi9/openjdk-21:1.21 + +ENV LANGUAGE='en_US:en' + + +COPY target/lib/* /deployments/lib/ +COPY target/*-runner.jar /deployments/quarkus-run.jar + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] diff --git a/builder-api/src/main/docker/Dockerfile.native b/builder-api/src/main/docker/Dockerfile.native new file mode 100644 index 0000000..a364311 --- /dev/null +++ b/builder-api/src/main/docker/Dockerfile.native @@ -0,0 +1,29 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. +# +# Before building the container image run: +# +# ./mvnw package -Dnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native -t quarkus/builder-api . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/builder-api +# +# The ` registry.access.redhat.com/ubi8/ubi-minimal:8.10` base image is based on UBI 9. +# To use UBI 8, switch to `quay.io/ubi8/ubi-minimal:8.10`. +### +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.10 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root --chmod=0755 target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/builder-api/src/main/docker/Dockerfile.native-micro b/builder-api/src/main/docker/Dockerfile.native-micro new file mode 100644 index 0000000..9ed68d2 --- /dev/null +++ b/builder-api/src/main/docker/Dockerfile.native-micro @@ -0,0 +1,32 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. +# It uses a micro base image, tuned for Quarkus native executables. +# It reduces the size of the resulting container image. +# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. +# +# Before building the container image run: +# +# ./mvnw package -Dnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/builder-api . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/builder-api +# +# The `quay.io/quarkus/quarkus-micro-image:2.0` base image is based on UBI 9. +# To use UBI 8, switch to `quay.io/quarkus/quarkus-micro-image:2.0`. +### +FROM quay.io/quarkus/quarkus-micro-image:2.0 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root --chmod=0755 target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/builder-api/src/main/java/org/acme/auth/AuthFilter.java b/builder-api/src/main/java/org/acme/auth/AuthFilter.java new file mode 100644 index 0000000..3e8aac1 --- /dev/null +++ b/builder-api/src/main/java/org/acme/auth/AuthFilter.java @@ -0,0 +1,43 @@ +package org.acme.auth; + +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseToken; +import jakarta.annotation.Priority; +import jakarta.ws.rs.Priorities; +import jakarta.ws.rs.container.ContainerRequestContext; +import jakarta.ws.rs.container.ContainerRequestFilter; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.Provider; + +import java.io.IOException; + +@Provider +@Priority(Priorities.AUTHENTICATION) +public class AuthFilter implements ContainerRequestFilter { + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + String authHeader = requestContext.getHeaderString("Authorization"); + + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + abortWithUnauthorized(requestContext); + return; + } + + String idToken = authHeader.substring("Bearer ".length()); + + try { + FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(idToken); + requestContext.setProperty("user", decodedToken); + } catch (Exception e) { + abortWithUnauthorized(requestContext); + } + } + + private void abortWithUnauthorized(ContainerRequestContext requestContext) { + requestContext.abortWith( + Response.status(Response.Status.UNAUTHORIZED) + .entity("Invalid or missing authorization token") + .build()); + } +} diff --git a/builder-api/src/main/java/org/acme/auth/AuthUtils.java b/builder-api/src/main/java/org/acme/auth/AuthUtils.java new file mode 100644 index 0000000..f8cadfa --- /dev/null +++ b/builder-api/src/main/java/org/acme/auth/AuthUtils.java @@ -0,0 +1,16 @@ +package org.acme.auth; + +import com.google.firebase.auth.FirebaseToken; +import jakarta.ws.rs.container.ContainerRequestContext; +import jakarta.ws.rs.core.Response; + +public class AuthUtils { + public static String getUserId(ContainerRequestContext requestContext){ + FirebaseToken userToken = (FirebaseToken) requestContext.getProperty("user"); + if (userToken == null) { + return null; + } + + return userToken.getUid(); + } +} diff --git a/builder-api/src/main/java/org/acme/constants/CollectionNames.java b/builder-api/src/main/java/org/acme/constants/CollectionNames.java new file mode 100644 index 0000000..7e03e24 --- /dev/null +++ b/builder-api/src/main/java/org/acme/constants/CollectionNames.java @@ -0,0 +1,6 @@ +package org.acme.constants; + +public class CollectionNames { + public static final String SCREENER_COLLECTION = "screener"; + public static final String DMN_MODEL_COLLECTION = "dmn_model"; +} diff --git a/builder-api/src/main/java/org/acme/constants/FieldNames.java b/builder-api/src/main/java/org/acme/constants/FieldNames.java new file mode 100644 index 0000000..4657224 --- /dev/null +++ b/builder-api/src/main/java/org/acme/constants/FieldNames.java @@ -0,0 +1,33 @@ +package org.acme.constants; + + +public class FieldNames { + public static final String ID = "id"; + + // Screener Database fields + public static final String WORKING_FORM_PATH = "working_form_path"; + public static final String SCREENER_NAME = "screener_name"; + public static final String IS_PUBLISHED = "is_published"; + public static final String WORKING_DMN_PATH = "working_dmn_path"; + public static final String OWNER_ID = "owner_id"; + public static final String LAST_PUBLISHED_DATE = "last_published_date"; + public static final String ORGANIZATION_NAME = "organization_name"; + public static final String WORKING_DMN_NAME = "working_dmn_name"; + public static final String PUBLISHED_DMN_NAME = "published_dmn_name"; + public static final String WORKING_DMN_NAMESPACE = "working_dmn_namespace"; + public static final String PUBLISHED_DMN_NAMESPACE = "published_dmn_namespace"; + public static final String LAST_DMN_SAVE = "last_dmn_save"; + public static final String LAST_DMN_COMPILE = "last_dmn_compile"; + public static final String DEPENDENCIES = "dependencies"; + + // DmnModel Database fields + public static final String GROUP_ID = "group_id"; + public static final String ARTIFACT_ID = "artifact_id"; + public static final String VERSION = "version"; + public static final String STORAGE_LOCATION = "storage_location"; + public static final String IMPORT_TYPE = "import_type"; + public static final String NAMESPACE = "namespace"; + public static final String MODEL_NAME = "name"; + public static final String MODEL_DESCRIPTION = "description"; + public static final String MODEL_SHORT_DESCRIPTION = "short_description"; +} \ No newline at end of file diff --git a/builder-api/src/main/java/org/acme/controller/DecisionResource.java b/builder-api/src/main/java/org/acme/controller/DecisionResource.java new file mode 100644 index 0000000..630d94a --- /dev/null +++ b/builder-api/src/main/java/org/acme/controller/DecisionResource.java @@ -0,0 +1,87 @@ +package org.acme.controller; + +import jakarta.inject.Inject; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.acme.model.domain.Screener; +import org.acme.persistence.ScreenerRepository; +import org.acme.persistence.StorageService; +import org.acme.service.DmnParser; +import org.acme.service.DmnService; + +import java.io.InputStream; +import java.time.Instant; +import java.util.*; + +@Path("/api/decision") +public class DecisionResource { + + @Inject + ScreenerRepository screenerRepository; + + @Inject + StorageService storageService; + + @Inject + DmnService dmnService; + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response post(@QueryParam("screenerId") String screenerId, Map inputData) { + if (screenerId == null || screenerId.isBlank()){ + return Response.status(Response.Status.BAD_REQUEST) + .entity("Error: Missing required query parameter: screenerId") + .build(); + } + + if (inputData == null || inputData.isEmpty()){ + return Response.status(Response.Status.BAD_REQUEST) + .entity("Error: Missing decision inputs") + .build(); + } + + return getScreenerResults(screenerId, inputData); + } + + private Response getScreenerResults(String screenerId, Map inputData) { + Optional screenerOptional = screenerRepository.getScreener(screenerId); + + if (screenerOptional.isEmpty()){ + throw new NotFoundException(String.format("Form %s was not found", screenerId)); + } + + Screener screener = screenerOptional.get(); + + try { + if (isLastScreenerCompileOutOfDate(screener)){ + dmnService.compileWorkingDmnModel(screener); + updateScreenerLastCompileTime(screenerId, screener.getDmnModel()); + } + + Map result = dmnService.evaluateDecision(screener, inputData); + + if (result.isEmpty()) return Response.ok().entity(Collections.emptyList()).build(); + + return Response.ok().entity(result).build(); + + } catch (Exception e){ + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + } + + private static boolean isLastScreenerCompileOutOfDate(Screener screener) { + return screener.getLastDmnCompile() == null || Instant.parse(screener.getLastDmnSave()).isAfter(Instant.parse(screener.getLastDmnCompile())); + } + + private void updateScreenerLastCompileTime(String screenerId, String dmnXml) throws Exception { + Screener updateScreener = new Screener(); + updateScreener.setId(screenerId); + updateScreener.setLastDmnCompile(Instant.now().toString()); + DmnParser dmnParser = new DmnParser(dmnXml); + updateScreener.setWorkingDmnName(dmnParser.getName()); + updateScreener.setWorkingDmnNameSpace(dmnParser.getNameSpace()); + screenerRepository.updateScreener(updateScreener); + } +} diff --git a/builder-api/src/main/java/org/acme/controller/ModelLibraryResource.java b/builder-api/src/main/java/org/acme/controller/ModelLibraryResource.java new file mode 100644 index 0000000..5a57f87 --- /dev/null +++ b/builder-api/src/main/java/org/acme/controller/ModelLibraryResource.java @@ -0,0 +1,36 @@ +package org.acme.controller; + +import io.quarkus.logging.Log; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.container.ContainerRequestContext; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.Response; +import org.acme.persistence.DmnModelRepository; +import org.acme.service.ModelLibraryService; + +@Path("/api") +public class ModelLibraryResource { + + @Inject + ModelLibraryService modelLibraryService; + + @GET + @Path("/dmn-models") + public Response getDmnModels(@Context ContainerRequestContext requestContext) { + Log.info("Get dmn models list"); + return modelLibraryService.getDmnModels(); + } + + @GET + @Path("/dmn-models/{groupId}/{artifactId}/{version}") + public Response getDmnModel( @PathParam("groupId") String groupId, + @PathParam("artifactId") String artifactId, + @PathParam("version") String version){ + + Log.info("Get dmn model"); + return modelLibraryService.getDmnModel(groupId, artifactId, version); + } +} diff --git a/builder-api/src/main/java/org/acme/controller/ScreenerResource.java b/builder-api/src/main/java/org/acme/controller/ScreenerResource.java new file mode 100644 index 0000000..94090af --- /dev/null +++ b/builder-api/src/main/java/org/acme/controller/ScreenerResource.java @@ -0,0 +1,333 @@ +package org.acme.controller; + +import com.fasterxml.jackson.databind.JsonNode; +import io.quarkus.logging.Log; +import jakarta.inject.Inject; +import jakarta.ws.rs.*; +import jakarta.ws.rs.container.ContainerRequestContext; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.acme.auth.AuthUtils; +import org.acme.model.dto.DmnImportRequest; +import org.acme.model.dto.PublishScreenerRequest; +import org.acme.model.dto.SaveDmnRequest; +import org.acme.model.dto.SaveSchemaRequest; +import org.acme.model.domain.Screener; +import org.acme.persistence.ScreenerRepository; +import org.acme.persistence.StorageService; +import org.acme.service.DmnService; +import org.acme.service.ScreenerDependencyService; +import org.acme.service.DmnParser; + +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@Path("/api") +public class ScreenerResource { + + @Inject + ScreenerRepository screenerRepository; + + @Inject + StorageService storageService; + + @Inject + DmnService dmnService; + + @Inject + ScreenerDependencyService screenerDependencyService; + + @GET + @Path("/screeners") + public Response getScreeners(@Context ContainerRequestContext requestContext) { + String userId = AuthUtils.getUserId(requestContext); + if (userId == null){ + return Response.status(Response.Status.UNAUTHORIZED).build(); + } + Log.info("Fetching screeners for user: " + userId); + List screeners = screenerRepository.getScreeners(userId); + + return Response.ok(screeners, MediaType.APPLICATION_JSON).build(); + } + + + @GET + @Path("/screener/{screenerId}") + public Response getScreener(@Context ContainerRequestContext requestContext, @PathParam("screenerId") String screenerId) { + + String userId = AuthUtils.getUserId(requestContext); + Log.info("Fetching screener " + screenerId + " for user " + userId); + + //perform authentication + + Optional screenerOptional = screenerRepository.getScreener(screenerId); + + if (screenerOptional.isEmpty()){ + throw new NotFoundException(); + } + + Screener screener = screenerOptional.get(); + + if (!isUserAuthorizedToAccessScreener(userId, screener)) return Response.status(Response.Status.UNAUTHORIZED).build(); + + return Response.ok(screener, MediaType.APPLICATION_JSON).build(); + } + + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Path("/screener") + public Response postScreener(@Context ContainerRequestContext requestContext, Screener newScreener){ + String userId = AuthUtils.getUserId(requestContext); + + //initialize screener data not in form + newScreener.setIsPublished(false); + + newScreener.setOwnerId(userId); + try { + String screenerId = screenerRepository.saveNewScreener(newScreener); + newScreener.setId(screenerId); + return Response.ok(newScreener, MediaType.APPLICATION_JSON).build(); + } catch (Exception e){ + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(Map.of("error", "Could not save Screener")) + .build(); + } + } + + + @PUT + @Consumes(MediaType.APPLICATION_JSON) + @Path("/screener") + public Response updateScreener(@Context ContainerRequestContext requestContext, Screener screener){ + String userId = AuthUtils.getUserId(requestContext); + if (!isUserAuthorizedToAccessScreener(userId, screener.getId())) return Response.status(Response.Status.UNAUTHORIZED).build(); + + //add user info to the update data + screener.setOwnerId(userId); + + Log.info("isPublished: " + screener.isPublished()); + try { + screenerRepository.updateScreener(screener); + + return Response.ok().build(); + } catch (Exception e){ + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(Map.of("error", "Could not update Screener")) + .build(); + } + } + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Path("/save-form-schema") + public Response saveFormSchema(@Context ContainerRequestContext requestContext, SaveSchemaRequest saveSchemaRequest){ + + String screenerId = saveSchemaRequest.screenerId; + if (screenerId == null || screenerId.isBlank()){ + return Response.status(Response.Status.BAD_REQUEST) + .entity("Error: Missing required required data in request body: screenerId") + .build(); + } + + String userId = AuthUtils.getUserId(requestContext); + if (!isUserAuthorizedToAccessScreener(userId, saveSchemaRequest.screenerId)) return Response.status(Response.Status.UNAUTHORIZED).build(); + + JsonNode schema = saveSchemaRequest.schema; + if (schema == null){ + return Response.status(Response.Status.BAD_REQUEST) + .entity("Error: Missing required required data in request body: screenerId") + .build(); + } + try { + String filePath = storageService.getScreenerWorkingFormSchemaPath(screenerId); + storageService.writeJsonToStorage(filePath, schema); + Log.info("Saved form schema of screener " + screenerId + " to storage"); + return Response.ok().build(); + } catch (Exception e){ + Log.info(("Failed to save form for screener " + screenerId)); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + } + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Path("/save-dmn-model") + public Response saveDmnModel(@Context ContainerRequestContext requestContext, SaveDmnRequest saveDmnRequest){ + + String screenerId = saveDmnRequest.screenerId; + String dmnModel = saveDmnRequest.dmnModel; + if (screenerId == null || screenerId.isBlank()){ + return Response.status(Response.Status.BAD_REQUEST) + .entity("Error: Missing required data: screenerId") + .build(); + } + + String userId = AuthUtils.getUserId(requestContext); + if (!isUserAuthorizedToAccessScreener(userId, screenerId)) return Response.status(Response.Status.UNAUTHORIZED).build(); + + if (dmnModel == null){ + return Response.status(Response.Status.BAD_REQUEST) + .entity("Error: Missing required data: DMN Model") + .build(); + } + try { + String filePath = storageService.getScreenerWorkingDmnModelPath(screenerId); + storageService.writeStringToStorage(filePath, dmnModel, "application/xml"); + Log.info("Saved DMN model of screener " + screenerId + " to storage"); + + + Screener updateScreener = new Screener(); + updateScreener.setId(screenerId); + updateScreener.setLastDmnSave(Instant.now().toString()); + //update screener metadata + screenerRepository.updateScreener(updateScreener); + return Response.ok().build(); + } catch (Exception e){ + Log.info(("Failed to save DMN model for screener " + screenerId)); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + } + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Path("/publish") + public Response publishScreener(@Context ContainerRequestContext requestContext, PublishScreenerRequest publishScreenerRequest){ + + String screenerId = publishScreenerRequest.screenerId; + if (screenerId == null || screenerId.isBlank()){ + return Response.status(Response.Status.BAD_REQUEST) + .entity("Error: Missing required query parameter: screenerId") + .build(); + } + + String userId = AuthUtils.getUserId(requestContext); + if (!isUserAuthorizedToAccessScreener(userId, screenerId)) return Response.status(Response.Status.UNAUTHORIZED).build(); + + try { + //update published form schema + storageService.updatePublishedFormSchemaArtifact(screenerId); + Log.info("Updated Screener " + screenerId + " to published."); + + //update published dmn model + String dmnXml = dmnService.compilePublishedDmnModel(screenerId); + + Screener updateScreener = new Screener(); + updateScreener.setId(screenerId); + updateScreener.setIsPublished(true); + updateScreener.setLastPublishDate(Instant.now().toString()); + DmnParser dmnParser = new DmnParser(dmnXml); + updateScreener.setPublishedDmnName(dmnParser.getName()); + updateScreener.setPublishedDmnNameSpace(dmnParser.getNameSpace()); + //update screener metadata + screenerRepository.updateScreener(updateScreener); + + Map responseData = new HashMap<>(); + responseData.put("screenerUrl", getScreenerUrl(screenerId)); + return Response.ok().entity(responseData).build(); + + } catch (Exception e){ + Log.error("Error: Error updating screener to published. Screener: " + screenerId); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + } + + private static String getScreenerUrl(String screenerId) { + return "screener/" + screenerId; + } + + @POST + @Path("/unpublish") + public Response unpublishScreener(@Context ContainerRequestContext requestContext, @QueryParam("screenerId") String screenerId){ + + if (screenerId == null || screenerId.isBlank()){ + return Response.status(Response.Status.BAD_REQUEST) + .entity("Error: Missing required query parameter: screenerId") + .build(); + } + + String userId = AuthUtils.getUserId(requestContext); + if (!isUserAuthorizedToAccessScreener(userId, screenerId)) return Response.status(Response.Status.UNAUTHORIZED).build(); + + try { + Screener updateScreener = new Screener(); + updateScreener.setId(screenerId); + updateScreener.setIsPublished(false); + screenerRepository.updateScreener(updateScreener); + Log.info("Updated Screener " + screenerId + " to unpublished."); + return Response.ok().build(); + + } catch (Exception e){ + Log.error("Error: Error updating screener to unpublished. Screener: " + screenerId); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + } + + @DELETE + @Path("/screener/delete") + public Response deleteScreener(@Context ContainerRequestContext requestContext, @QueryParam("screenerId") String screenerId){ + if (screenerId == null || screenerId.isBlank()){ + return Response.status(Response.Status.BAD_REQUEST) + .entity("Error: Missing required query parameter: screenerId") + .build(); + } + + String userId = AuthUtils.getUserId(requestContext); + if (!isUserAuthorizedToAccessScreener(userId, screenerId)) return Response.status(Response.Status.UNAUTHORIZED).build(); + + try { + screenerRepository.deleteScreener(screenerId); + return Response.ok().build(); + } catch (Exception e){ + Log.error("Error: error deleting screener " + screenerId); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + } + + + private boolean isUserAuthorizedToAccessScreener(String userId, Screener screener) { + String ownerId = screener.getOwnerId(); + if (userId.equals(ownerId)){ + return true; + } + return false; + } + + private boolean isUserAuthorizedToAccessScreener(String userId, String screenerId) { + Optional screenerOptional = screenerRepository.getScreenerMetaDataOnly(screenerId); + if (screenerOptional.isEmpty()){ + return false; + } + Screener screener = screenerOptional.get(); + String ownerId = screener.getOwnerId(); + if (userId.equals(ownerId)){ + return true; + } + return false; + } + + // This Endpoint allows users to add a public DMN model into their project as a dependency. + // This makes the dmn model elements available in the dmn editor as well as includes the dmn model when the dmn is + // compiled + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Path("/dependency") + public Response addDependency(@Context ContainerRequestContext requestContext, DmnImportRequest request){ + String userId = AuthUtils.getUserId(requestContext); + return screenerDependencyService.addDependency(request, userId); + } + + // This Endpoint allows users to delete dmn dependencies from their project. The DMN model elements will no longer + // be available in the DMN editor. + @DELETE + @Consumes(MediaType.APPLICATION_JSON) + @Path("/dependency") + public Response deleteDependency(@Context ContainerRequestContext requestContext, DmnImportRequest request){ + String userId = AuthUtils.getUserId(requestContext); + return screenerDependencyService.deleteDependency(request, userId); + } +} diff --git a/builder-api/src/main/java/org/acme/functions/LocationService.java b/builder-api/src/main/java/org/acme/functions/LocationService.java new file mode 100644 index 0000000..e54a3dc --- /dev/null +++ b/builder-api/src/main/java/org/acme/functions/LocationService.java @@ -0,0 +1,86 @@ +package org.acme.functions; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.Unremovable; +import io.agroal.api.AgroalDataSource; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Unremovable +@ApplicationScoped +public class LocationService { + + @Inject + AgroalDataSource dataSource; + + public Connection getDbConnection() throws SQLException { + return dataSource.getConnection(); + } + + public static List lookup(String column, Map filters) { + List results = new ArrayList<>(); + StringBuilder sql = new StringBuilder("SELECT DISTINCT ").append(column).append(" FROM locations WHERE "); + + // construct WHERE clause; assume everything is ANDed together + boolean first = true; + for (String key : filters.keySet()) { + if (!first) { + sql.append(" AND "); + } + sql.append(key).append(" = ?"); + first = false; + } + + LocationService service = Arc.container().instance(LocationService.class).get(); + + Connection connection = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + + try { + connection = service.getDbConnection(); + pstmt = connection.prepareStatement(sql.toString()); + + // Set the values dynamically + int index = 1; + for (Object value : filters.values()) { + String stringValue = value.toString(); // db only has strings + pstmt.setString(index++, stringValue); + } + + rs = pstmt.executeQuery(); + + while(rs.next()) { + String thisString = rs.getString(column); + results.add(thisString); + } + } catch (SQLException e) { + e.printStackTrace(); + } finally { + try { + if (rs != null) { + rs.close(); + } + if (pstmt != null) { + pstmt.close(); + } + if (connection != null) { + connection.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + + return results; + + } +} diff --git a/builder-api/src/main/java/org/acme/infrastructure/FirebaseInitializer.java b/builder-api/src/main/java/org/acme/infrastructure/FirebaseInitializer.java new file mode 100644 index 0000000..78bb9dc --- /dev/null +++ b/builder-api/src/main/java/org/acme/infrastructure/FirebaseInitializer.java @@ -0,0 +1,35 @@ +package org.acme.infrastructure; + +import com.google.auth.oauth2.GoogleCredentials; +import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseOptions; +import io.quarkus.runtime.Startup; +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; + +import java.io.IOException; + +@Startup +@ApplicationScoped +public class FirebaseInitializer { + + @PostConstruct + void init() { + + if (FirebaseApp.getApps().isEmpty()) { + try { + + FirebaseOptions options = new FirebaseOptions.Builder() + .setCredentials(GoogleCredentials.getApplicationDefault()) + .setProjectId("benefit-decision-toolkit-play") + .setStorageBucket("benefit-decision-toolkit-play.firebasestorage.app") + .build(); + + FirebaseApp.initializeApp(options); + System.out.println("✅ Firebase initialized"); + } catch (IOException e) { + throw new RuntimeException("Failed to initialize Firebase", e); + } + } + } +} \ No newline at end of file diff --git a/builder-api/src/main/java/org/acme/mapper/DmnModelMapper.java b/builder-api/src/main/java/org/acme/mapper/DmnModelMapper.java new file mode 100644 index 0000000..a8a036e --- /dev/null +++ b/builder-api/src/main/java/org/acme/mapper/DmnModelMapper.java @@ -0,0 +1,88 @@ +package org.acme.mapper; + +import org.acme.constants.FieldNames; +import org.acme.model.domain.DmnModel; +import org.acme.model.dto.DmnModelSummary; + +import java.util.HashMap; +import java.util.Map; + +public class DmnModelMapper { + + public static DmnModel fromMap(Map map) { + DmnModel dmnModel = new DmnModel(); + + if (doesAttributeExistOfType(map, FieldNames.ID, String.class)) { + dmnModel.setId((String) map.get(FieldNames.ID)); + } + if (doesAttributeExistOfType(map, FieldNames.GROUP_ID, String.class)) { + dmnModel.setGroupId((String) map.get(FieldNames.GROUP_ID)); + } + if (doesAttributeExistOfType(map, FieldNames.ARTIFACT_ID, String.class)) { + dmnModel.setArtifactId((String) map.get(FieldNames.ARTIFACT_ID)); + } + if (doesAttributeExistOfType(map, FieldNames.VERSION, String.class)){ + dmnModel.setVersion((String) map.get(FieldNames.VERSION)); + } + if (doesAttributeExistOfType(map, FieldNames.IMPORT_TYPE, String.class)){ + dmnModel.setImportType((String) map.get(FieldNames.IMPORT_TYPE)); + } + if (doesAttributeExistOfType(map, FieldNames.NAMESPACE, String.class)){ + dmnModel.setNamespace((String) map.get(FieldNames.NAMESPACE)); + } + if (doesAttributeExistOfType(map, FieldNames.STORAGE_LOCATION, String.class)) { + dmnModel.setStorageLocation((String) map.get(FieldNames.STORAGE_LOCATION)); + } + if (doesAttributeExistOfType(map, FieldNames.MODEL_DESCRIPTION, String.class)) { + dmnModel.setDescription((String) map.get(FieldNames.MODEL_DESCRIPTION)); + } + if (doesAttributeExistOfType(map, FieldNames.MODEL_SHORT_DESCRIPTION, String.class)) { + dmnModel.setShortDescription((String) map.get(FieldNames.MODEL_SHORT_DESCRIPTION)); + } + if (doesAttributeExistOfType(map, FieldNames.MODEL_NAME, String.class)) { + dmnModel.setName((String) map.get(FieldNames.MODEL_NAME)); + } + return dmnModel; + } + + public static Map fromDmnModel(DmnModel model) { + Map data = new HashMap<>(); + if (model.getGroupId() != null) { + data.put(FieldNames.GROUP_ID, model.getGroupId()); + } + if (model.getArtifactId() != null) { + data.put(FieldNames.ARTIFACT_ID, model.getArtifactId()); + } + if (model.getVersion() != null) { + data.put(FieldNames.VERSION, model.getVersion()); + } + return data; + } + + public static DmnModelSummary summaryFromDmnModel(DmnModel model) { + DmnModelSummary summary = new DmnModelSummary(); + if (model.getGroupId() != null) { + summary.groupId = model.getGroupId(); + } + if (model.getArtifactId() != null) { + summary.artifactId = model.getArtifactId(); + } + if (model.getVersion() != null) { + summary.version = model.getVersion(); + } + if (model.getName() != null) { + summary.name = model.getName(); + } + if (model.getDescription() != null) { + summary.description = model.getDescription(); + } + if (model.getShortDescription() != null) { + summary.shortDescription = model.getShortDescription(); + } + return summary; + } + + private static boolean doesAttributeExistOfType(Map map, String attributeName, Class expectedType) { + return map.containsKey(attributeName) && expectedType.isInstance(map.get(attributeName)); + } +} diff --git a/builder-api/src/main/java/org/acme/mapper/ScreenerMapper.java b/builder-api/src/main/java/org/acme/mapper/ScreenerMapper.java new file mode 100644 index 0000000..e6cc4a3 --- /dev/null +++ b/builder-api/src/main/java/org/acme/mapper/ScreenerMapper.java @@ -0,0 +1,125 @@ +package org.acme.mapper; + +import com.github.javaparser.utils.Log; +import org.acme.constants.FieldNames; +import org.acme.model.domain.Screener; +import org.acme.model.dto.Dependency; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ScreenerMapper { + + public static Screener fromMap(Map map){ + Screener screener = new Screener(); + + if (doesAttributeExistOfType(map, FieldNames.ID, String.class)){ + screener.setId((String) map.get(FieldNames.ID)); + } + if (doesAttributeExistOfType(map, FieldNames.OWNER_ID, String.class)){ + screener.setOwnerId((String) map.get(FieldNames.OWNER_ID)); + } + if (doesAttributeExistOfType(map, FieldNames.SCREENER_NAME, String.class)){ + screener.setScreenerName((String) map.get(FieldNames.SCREENER_NAME)); + } + if (doesAttributeExistOfType(map, FieldNames.LAST_PUBLISHED_DATE, String.class)){ + screener.setLastPublishDate((String) map.get(FieldNames.LAST_PUBLISHED_DATE)); + } + if (doesAttributeExistOfType(map, FieldNames.IS_PUBLISHED, Boolean.class)){ + screener.setIsPublished((Boolean) map.get(FieldNames.IS_PUBLISHED)); + } + if (doesAttributeExistOfType(map, FieldNames.WORKING_DMN_NAME, String.class)){ + screener.setWorkingDmnName((String) map.get(FieldNames.WORKING_DMN_NAME)); + } + if (doesAttributeExistOfType(map, FieldNames.WORKING_DMN_NAMESPACE, String.class)){ + screener.setWorkingDmnNameSpace((String) map.get(FieldNames.WORKING_DMN_NAMESPACE)); + } + if (doesAttributeExistOfType(map, FieldNames.PUBLISHED_DMN_NAME, String.class)){ + screener.setPublishedDmnName((String) map.get(FieldNames.PUBLISHED_DMN_NAME)); + } + if (doesAttributeExistOfType(map, FieldNames.PUBLISHED_DMN_NAMESPACE, String.class)){ + screener.setPublishedDmnNameSpace((String) map.get(FieldNames.PUBLISHED_DMN_NAMESPACE)); + } + if (doesAttributeExistOfType(map, FieldNames.LAST_DMN_SAVE, String.class)){ + screener.setLastDmnSave((String) map.get(FieldNames.LAST_DMN_SAVE)); + } + if (doesAttributeExistOfType(map, FieldNames.LAST_DMN_COMPILE, String.class)){ + screener.setLastDmnCompile((String) map.get(FieldNames.LAST_DMN_COMPILE)); + } + if (doesAttributeExistOfType(map, FieldNames.DEPENDENCIES, List.class)) { + List rawDependencies = (List) map.get(FieldNames.DEPENDENCIES); + screener.setDependencies(mapDependencyFromMap(rawDependencies)); + } + + return screener; + } + + public static Map fromScreener(Screener screener){ + Map data = new HashMap<>(); + if (screener.getScreenerName() != null){ + data.put(FieldNames.SCREENER_NAME, screener.getScreenerName()); + } + if (screener.getOwnerId() != null){ + data.put(FieldNames.OWNER_ID, screener.getOwnerId()); + } + if (screener.isPublished() != null){ + data.put(FieldNames.IS_PUBLISHED, screener.isPublished()); + } + if (screener.getOrganizationName() != null){ + data.put(FieldNames.ORGANIZATION_NAME, screener.getOrganizationName()); + } + if (screener.getLastPublishDate() != null){ + data.put(FieldNames.LAST_PUBLISHED_DATE, screener.getLastPublishDate()); + } + if (screener.getWorkingDmnName() !=null){ + data.put(FieldNames.WORKING_DMN_NAME, screener.getWorkingDmnName()); + } + if (screener.getWorkingDmnNameSpace() !=null){ + data.put(FieldNames.WORKING_DMN_NAMESPACE, screener.getWorkingDmnNameSpace()); + } + if (screener.getPublishedDmnName() !=null){ + data.put(FieldNames.PUBLISHED_DMN_NAME, screener.getPublishedDmnName()); + } + if (screener.getPublishedDmnNameSpace() !=null){ + data.put(FieldNames.PUBLISHED_DMN_NAMESPACE, screener.getPublishedDmnNameSpace()); + } + if (screener.getLastDmnSave()!=null){ + data.put(FieldNames.LAST_DMN_SAVE, screener.getLastDmnSave()); + } + if (screener.getLastDmnCompile()!=null){ + data.put(FieldNames.LAST_DMN_COMPILE, screener.getLastDmnCompile()); + } + + return data; + } + + private static List mapDependencyFromMap(List dependenciesRaw){ + List dependencies = new ArrayList<>(); + + for (Object rawItem : dependenciesRaw) { + if (rawItem instanceof Map) { + Map dependencyMap = (Map) rawItem; + Dependency dependency = new Dependency(); + + if (dependencyMap.get(FieldNames.GROUP_ID) instanceof String) { + dependency.groupId = (String) dependencyMap.get(FieldNames.GROUP_ID); + } + if (dependencyMap.get(FieldNames.ARTIFACT_ID) instanceof String) { + dependency.artifactId = (String) dependencyMap.get(FieldNames.ARTIFACT_ID); + } + if (dependencyMap.get(FieldNames.VERSION) instanceof String) { + dependency.version = (String) dependencyMap.get(FieldNames.VERSION); + } + dependencies.add(dependency); + } + } + + return dependencies; + } + + private static boolean doesAttributeExistOfType(Map map, String attributeName, Class expectedType) { + return map.containsKey(attributeName) && expectedType.isInstance(map.get(attributeName)); + } +} diff --git a/builder-api/src/main/java/org/acme/model/domain/DmnModel.java b/builder-api/src/main/java/org/acme/model/domain/DmnModel.java new file mode 100644 index 0000000..ac45b9e --- /dev/null +++ b/builder-api/src/main/java/org/acme/model/domain/DmnModel.java @@ -0,0 +1,94 @@ +package org.acme.model.domain; + +public class DmnModel { + private String id; + private String groupId; + private String artifactId; + private String version; + private String storageLocation; + private String importType; + private String namespace; + private String description; + private String shortDescription; + private String name; + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getArtifactId() { + return artifactId; + } + + public void setArtifactId(String artifactId) { + this.artifactId = artifactId; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getStorageLocation() { + return storageLocation; + } + + public void setStorageLocation(String storageLocation) { + this.storageLocation = storageLocation; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getImportType() { + return importType; + } + + public void setImportType(String importType) { + this.importType = importType; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getShortDescription() { + return shortDescription; + } + + public void setShortDescription(String shortDescription) { + this.shortDescription = shortDescription; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/builder-api/src/main/java/org/acme/model/domain/Screener.java b/builder-api/src/main/java/org/acme/model/domain/Screener.java new file mode 100644 index 0000000..5b61235 --- /dev/null +++ b/builder-api/src/main/java/org/acme/model/domain/Screener.java @@ -0,0 +1,155 @@ +package org.acme.model.domain; +import org.acme.model.dto.Dependency; + +import java.util.List; +import java.util.Map; + +public class Screener { + private String id; + private Map formSchema; + private String dmnModel; + private Boolean isPublished; + private String ownerId; + private String screenerName; + private String lastPublishDate; + private String organizationName; + private String publishedDmnNameSpace; + private String workingDmnNameSpace; + private String publishedDmnName; + private String workingDmnName; + private String lastDmnSave; + private String lastDmnCompile; + private List dependencies; + + public Screener(Map model, boolean isPublished){ + this.formSchema = model; + this.isPublished = isPublished; + } + + public Screener(){ + } + + public Map getFormSchema() { + return formSchema; + } + + public Boolean isPublished() { + return isPublished; + } + + public void setIsPublished(Boolean isPublished){ + this.isPublished = isPublished; + } + + public void setFormSchema(Map formSchema) { + this.formSchema = formSchema; + } + + public void setDmnModel(String dmnModel){ + this.dmnModel = dmnModel; + } + + public void setOwnerId(String ownerId){ + this.ownerId = ownerId; + } + + public String getOwnerId(){ + return this.ownerId; + } + + public void setScreenerName(String screenerName){ + this.screenerName = screenerName; + } + + public String getScreenerName(){ + return this.screenerName; + } + + public void setLastPublishDate(String lastPublishDate){ + this.lastPublishDate = lastPublishDate; + } + + public void setId(String id){ + this.id = id; + } + + public String getId(){ + return this.id; + } + + public String getDmnModel(){ + return this.dmnModel; + } + + public String getOrganizationName(){ + return this.organizationName; + } + + public void setOrganizationName(String organizationName){ + this.organizationName = organizationName; + } + + public void setLastPublishedDate(String lastPublishDate){ + this.lastPublishDate = lastPublishDate; + } + + public String getLastPublishDate(){ + return this.lastPublishDate; + } + + public String getPublishedDmnNameSpace(){ + return this.publishedDmnNameSpace; + } + + public String getPublishedDmnName(){ + return this.publishedDmnName; + } + + public void setPublishedDmnName(String name){ + this.publishedDmnName = name; + } + + public void setPublishedDmnNameSpace(String nameSpace){ + this.publishedDmnNameSpace = nameSpace; + } + + public String getLastDmnSave() { + return lastDmnSave; + } + + public void setLastDmnSave(String lastDmnSave) { + this.lastDmnSave = lastDmnSave; + } + + public String getLastDmnCompile() { + return lastDmnCompile; + } + + public void setLastDmnCompile(String lastDmnCompile) { + this.lastDmnCompile = lastDmnCompile; + } + + public String getWorkingDmnNameSpace() { + return workingDmnNameSpace; + } + + public void setWorkingDmnNameSpace(String workingDmnNameSpace) { + this.workingDmnNameSpace = workingDmnNameSpace; + } + + public String getWorkingDmnName() { + return workingDmnName; + } + + public void setWorkingDmnName(String workingDmnName) { + this.workingDmnName = workingDmnName; + } + + public List getDependencies() { + return dependencies; + } + + public void setDependencies(List dependencies) { + this.dependencies = dependencies; + } +} diff --git a/builder-api/src/main/java/org/acme/model/dto/Dependency.java b/builder-api/src/main/java/org/acme/model/dto/Dependency.java new file mode 100644 index 0000000..0ed9fa9 --- /dev/null +++ b/builder-api/src/main/java/org/acme/model/dto/Dependency.java @@ -0,0 +1,8 @@ +package org.acme.model.dto; + +public class Dependency { + public String groupId; + public String artifactId; + public String version; + public String xml; +} diff --git a/builder-api/src/main/java/org/acme/model/dto/DmnImportRequest.java b/builder-api/src/main/java/org/acme/model/dto/DmnImportRequest.java new file mode 100644 index 0000000..877bc6c --- /dev/null +++ b/builder-api/src/main/java/org/acme/model/dto/DmnImportRequest.java @@ -0,0 +1,8 @@ +package org.acme.model.dto; + +public class DmnImportRequest { + public String screenerId; + public String groupId; + public String artifactId; + public String version; +} diff --git a/builder-api/src/main/java/org/acme/model/dto/DmnModelSummary.java b/builder-api/src/main/java/org/acme/model/dto/DmnModelSummary.java new file mode 100644 index 0000000..20f2765 --- /dev/null +++ b/builder-api/src/main/java/org/acme/model/dto/DmnModelSummary.java @@ -0,0 +1,10 @@ +package org.acme.model.dto; + +public class DmnModelSummary { + public String groupId; + public String artifactId; + public String version; + public String description; + public String shortDescription; + public String name; +} diff --git a/builder-api/src/main/java/org/acme/model/dto/PublishScreenerRequest.java b/builder-api/src/main/java/org/acme/model/dto/PublishScreenerRequest.java new file mode 100644 index 0000000..d1bcdf2 --- /dev/null +++ b/builder-api/src/main/java/org/acme/model/dto/PublishScreenerRequest.java @@ -0,0 +1,5 @@ +package org.acme.model.dto; + +public class PublishScreenerRequest { + public String screenerId; +} diff --git a/builder-api/src/main/java/org/acme/model/dto/SaveDmnRequest.java b/builder-api/src/main/java/org/acme/model/dto/SaveDmnRequest.java new file mode 100644 index 0000000..f6fcb6a --- /dev/null +++ b/builder-api/src/main/java/org/acme/model/dto/SaveDmnRequest.java @@ -0,0 +1,6 @@ +package org.acme.model.dto; + +public class SaveDmnRequest { + public String screenerId; + public String dmnModel; +} diff --git a/builder-api/src/main/java/org/acme/model/dto/SaveSchemaRequest.java b/builder-api/src/main/java/org/acme/model/dto/SaveSchemaRequest.java new file mode 100644 index 0000000..4724484 --- /dev/null +++ b/builder-api/src/main/java/org/acme/model/dto/SaveSchemaRequest.java @@ -0,0 +1,8 @@ +package org.acme.model.dto; + +import com.fasterxml.jackson.databind.JsonNode; + +public class SaveSchemaRequest { + public String screenerId; + public JsonNode schema; +} diff --git a/builder-api/src/main/java/org/acme/persistence/DmnModelRepository.java b/builder-api/src/main/java/org/acme/persistence/DmnModelRepository.java new file mode 100644 index 0000000..a41a07b --- /dev/null +++ b/builder-api/src/main/java/org/acme/persistence/DmnModelRepository.java @@ -0,0 +1,13 @@ +package org.acme.persistence; + +import org.acme.model.domain.DmnModel; + +import java.util.List; +import java.util.Optional; + +public interface DmnModelRepository { + + public List getAllDmnModels(); + + public Optional getDmnModel(String groupId, String artifactId, String version); +} diff --git a/builder-api/src/main/java/org/acme/persistence/DmnModelRespositoryImpl.java b/builder-api/src/main/java/org/acme/persistence/DmnModelRespositoryImpl.java new file mode 100644 index 0000000..bbca0b4 --- /dev/null +++ b/builder-api/src/main/java/org/acme/persistence/DmnModelRespositoryImpl.java @@ -0,0 +1,55 @@ +package org.acme.persistence; + +import jakarta.enterprise.context.ApplicationScoped; +import org.acme.constants.CollectionNames; +import org.acme.mapper.DmnModelMapper; +import org.acme.model.domain.DmnModel; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@ApplicationScoped +public class DmnModelRespositoryImpl implements DmnModelRepository{ + @Override + public List getAllDmnModels() { + List> dmnMaps = FirestoreUtils.getAllDocsInCollection( + CollectionNames.DMN_MODEL_COLLECTION); + + return dmnMaps.stream().map(DmnModelMapper::fromMap).toList(); + } + + + public Optional getDmnModel(String groupId, String artifactId, String version){ + + validateKeyComponent("groupId", groupId); + validateKeyComponent("artifactId", artifactId); + validateKeyComponent("version", version); + + String key = getDmnModelId(groupId, artifactId, version); + + Optional> modelOptional = FirestoreUtils.getFirestoreDocById(CollectionNames.DMN_MODEL_COLLECTION, key); + + if (modelOptional.isPresent()){ + DmnModel model = DmnModelMapper.fromMap(modelOptional.get()); + return Optional.of(model); + } else { + return Optional.empty(); + } + } + + private static String getDmnModelId(String groupId, String artifactId, String version) { + String key = String.join(":", groupId, artifactId, version); + return key; + } + + private void validateKeyComponent(String name, String value) { + if (value == null || value.isEmpty()) { + throw new IllegalArgumentException(name + " must not be null or empty"); + } + if (value.contains(":") || value.contains("/") || value.length() > 100) { + throw new IllegalArgumentException(name + " contains invalid characters or is too long"); + } + } + +} diff --git a/builder-api/src/main/java/org/acme/persistence/FirestoreUtils.java b/builder-api/src/main/java/org/acme/persistence/FirestoreUtils.java new file mode 100644 index 0000000..eacc224 --- /dev/null +++ b/builder-api/src/main/java/org/acme/persistence/FirestoreUtils.java @@ -0,0 +1,168 @@ +package org.acme.persistence; + +import com.google.api.core.ApiFuture; +import com.google.cloud.firestore.*; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.Bucket; +import com.google.firebase.cloud.FirestoreClient; +import com.google.firebase.cloud.StorageClient; +import io.quarkus.logging.Log; + +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ExecutionException; + +public class FirestoreUtils { + + private static final Firestore db = FirestoreClient.getFirestore(); + + public static List> getAllDocsInCollection(String collection){ + try { + ApiFuture query = db.collection(collection) + .get(); + List documents; + documents = query.get().getDocuments(); + + return documents.stream() + .map(doc -> { + Map data = doc.getData(); + data.put("id", doc.getId()); + return data; + }) + .toList(); + + }catch(Exception e){ + Log.error("Error fetching documents from firestore: ", e); + return new ArrayList<>(); + } + } + + public static List> getFirestoreDocsByField(String collection, String field, String value) { + try { + ApiFuture query = db.collection(collection) + .whereEqualTo(field, value) + .get(); + List documents; + documents = query.get().getDocuments(); + + return documents.stream() + .map(doc -> { + Map data = doc.getData(); + data.put("id", doc.getId()); + return data; + }) + .toList(); + + }catch(Exception e){ + Log.error("Error fetching documents from firestore: ", e); + return new ArrayList<>(); + } + } + + public static Optional> getFirestoreDocById(String collection, String id) { + try { + + DocumentSnapshot doc = db.collection(collection) + .document(id) + .get().get(); + + + if (!doc.exists()) { + return Optional.empty(); + } + + + Map data = doc.getData(); + data.put("id", doc.getId()); + + return Optional.of(data); + + }catch(Exception e){ + Log.error("Error fetching document from firestore: ", e); + return Optional.empty(); + } + } + + public static Optional getFileAsStringFromStorage(String filePath) { + try { + Bucket bucket = StorageClient.getInstance().bucket(); + Blob blob = bucket.get(filePath); + + if (blob == null || !blob.exists()) { + return Optional.empty(); + } + + byte[] data = blob.getContent(); + String content = new String(data, StandardCharsets.UTF_8); + + return Optional.of(content); + + } catch (Exception e){ + Log.error("Error fetching file from firebase storage: ", e); + return Optional.empty(); + } + } + + public static String persistDocument(String collectionName, Map data) throws Exception { + try { + DocumentReference documentRef = db.collection(collectionName) + .add(data) + .get(); + return documentRef.getId(); + + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); // preserve interrupt status + throw new Exception("Thread interrupted while saving to Firestore", e); + } catch (ExecutionException e) { + throw new Exception("Failed to write document to Firestore", e); + } + } + + + public static void updateDocument(String collectionName, Map data, String docId) throws Exception { + try { + WriteResult result = db.collection(collectionName) + .document(docId) + .set(data, SetOptions.merge()) + .get(); + Log.info("Document " + docId + " updated at " + result.getUpdateTime()); + + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); // preserve interrupt status + throw new Exception("Thread interrupted while saving to Firestore", e); + } catch (ExecutionException e) { + throw new Exception("Failed to write document to Firestore", e); + } + } + + public static void deleteDocument(String collectionName, String docId) throws Exception { + try{ + WriteResult result = db.collection(collectionName).document(docId).delete().get(); + Log.info("Document " + docId + " deleted at " + result.getUpdateTime()); + } catch (Exception e){ + Log.error("Failed to delete document from firestore"); + throw new Exception(e); + } + + } + + public static void addObjectToListFieldOfDocument(String collection, String docId, String field, Object object) throws Exception{ + try{ + DocumentReference docRef = db.collection(collection).document(docId); + docRef.update(field, FieldValue.arrayUnion(object)); + } catch (Exception e){ + Log.error("Failed to add object to list field of collection."); + throw new Exception(e); + } + } + + public static void removeObjectFromListFieldOfDocument(String collection, String docId, String field, Object object) throws Exception { + try { + DocumentReference docRef = db.collection(collection).document(docId); + docRef.update(field, FieldValue.arrayRemove(object)); + } catch (Exception e) { + Log.error("Failed to remove object from list field of collection."); + throw new Exception(e); + } + } +} diff --git a/builder-api/src/main/java/org/acme/persistence/GoogleStorageService.java b/builder-api/src/main/java/org/acme/persistence/GoogleStorageService.java new file mode 100644 index 0000000..da787e2 --- /dev/null +++ b/builder-api/src/main/java/org/acme/persistence/GoogleStorageService.java @@ -0,0 +1,181 @@ +package org.acme.persistence; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.cloud.storage.*; +import com.google.firebase.cloud.StorageClient; +import io.quarkus.logging.Log; +import jakarta.enterprise.context.ApplicationScoped; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.Optional; + +@ApplicationScoped +public class GoogleStorageService implements StorageService { + + @Override + public void writeStringToStorage(String filePath, String content, String contentType){ + try { + Bucket bucket = StorageClient.getInstance().bucket(); + InputStream inputSteam = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)); + Blob blob = bucket.create(filePath, inputSteam, contentType); + Log.info("Uploaded to GCS: " + blob.getName()); + } catch (Exception e){ + Log.error("Error writing file to GCS: " + e.getMessage()); + } + } + + @Override + public void writeBytesToStorage(String filePath, byte[] content, String contentType){ + try { + Bucket bucket = StorageClient.getInstance().bucket(); + InputStream inputSteam = new ByteArrayInputStream(content); + Blob blob = bucket.create(filePath, inputSteam, contentType); + Log.info("Uploaded to GCS: " + blob.getName()); + } catch (Exception e){ + Log.error("Error writing file to GCS: " + e.getMessage()); + } + } + + @Override + public void writeJsonToStorage(String filePath, JsonNode json){ + try { + Bucket bucket = StorageClient.getInstance().bucket(); + ObjectMapper mapper = new ObjectMapper(); + InputStream inputSteam = new ByteArrayInputStream(mapper.writeValueAsBytes(json)); + Blob blob = bucket.create(filePath, inputSteam, "application/json"); + Log.info("Uploaded to GCS: " + blob.getName()); + } catch (Exception e){ + Log.error("Error writing file to GCS: " + e.getMessage()); + } + } + + @Override + public Optional getFileInputStreamFromStorage(String filePath) { + try { + Bucket bucket = StorageClient.getInstance().bucket(); + Blob blob = bucket.get(filePath); + + if (blob == null || !blob.exists()) { + return Optional.empty(); + } + + byte[] data = blob.getContent(); + + return Optional.of(new ByteArrayInputStream(data)); + + } catch (Exception e){ + Log.error("Error fetching file from firebase storage: ", e); + return Optional.empty(); + } + } + + + @Override + public Optional getStringFromStorage(String filePath) { + try { + Bucket bucket = StorageClient.getInstance().bucket(); + Blob blob = bucket.get(filePath); + + if (blob == null || !blob.exists()) { + return Optional.empty(); + } + + // Get content and convert to UTF-8 String + byte[] data = blob.getContent(Blob.BlobSourceOption.generationMatch()); + String content = new String(data, StandardCharsets.UTF_8); + + return Optional.of(content); + + } catch (Exception e) { + Log.error("Error fetching XML file from Firebase Storage: ", e); + return Optional.empty(); + } + } + + @Override + public Optional getFileBytesFromStorage(String filePath) { + try { + Bucket bucket = StorageClient.getInstance().bucket(); + Blob blob = bucket.get(filePath); + + if (blob == null || !blob.exists()) { + return Optional.empty(); + } + + byte[] data = blob.getContent(); + + return Optional.of(data); + + } catch (Exception e){ + Log.error("Error fetching file from firebase storage: ", e); + return Optional.empty(); + } + } + @Override + public String getScreenerWorkingDmnModelPath(String screenerId){ + return "dmn/working/" + screenerId + ".dmn"; + } + + @Override + public String getScreenerWorkingFormSchemaPath(String screenerId){ + return "form/working/" + screenerId + ".json"; + } + + @Override + public String getScreenerPublishedFormSchemaPath(String screenerId){ + return "form/published/" + screenerId + ".json"; + } + + @Override + public String getPublishedCompiledDmnModelPath(String screenerId){ + return "compiled_dmn_models/published/" + screenerId + "/kiebase.ser"; + } + + @Override + public String getWorkingCompiledDmnModelPath(String screenerId){ + return "compiled_dmn_models/working/" + screenerId + "/kiebase.ser"; + } + @Override + public Map getFormSchemaFromStorage(String filePath) { + try { + Bucket bucket = StorageClient.getInstance().bucket(); + Blob blob = bucket.get(filePath); + + if (blob == null || !blob.exists()) { + return null; + } + + byte[] content = blob.getContent(); + + + ObjectMapper mapper = new ObjectMapper(); + Map formSchema = mapper.readValue(new ByteArrayInputStream(content), new TypeReference>() { + }); + + return formSchema; + + } catch (Exception e){ + Log.error("Error fetching form model from firebase storage: ", e); + return null; + } + } + + @Override + public void updatePublishedFormSchemaArtifact(String screenerId) throws Exception { + try { + Bucket bucket = StorageClient.getInstance().bucket(); + Blob workingFormBlob = bucket.get(getScreenerWorkingFormSchemaPath(screenerId)); + CopyWriter formCopyWriter = workingFormBlob.copyTo(BlobId.of(bucket.getName(), getScreenerPublishedFormSchemaPath(screenerId))); + formCopyWriter.getResult(); + Log.info("Working form schema copied to published artifact path for screener: " + screenerId); + } catch (Exception e){ + Log.error("Error updating published form schema in cloud storage"); + throw new Exception("Error updating published form schema in cloud storage"); + } + } +} diff --git a/builder-api/src/main/java/org/acme/persistence/ScreenerRepository.java b/builder-api/src/main/java/org/acme/persistence/ScreenerRepository.java new file mode 100644 index 0000000..1e24e4a --- /dev/null +++ b/builder-api/src/main/java/org/acme/persistence/ScreenerRepository.java @@ -0,0 +1,26 @@ +package org.acme.persistence; + +import org.acme.model.domain.DmnModel; +import org.acme.model.domain.Screener; + +import java.util.List; +import java.util.Optional; + +public interface ScreenerRepository { + + public List getScreeners(String userId); + + public Optional getScreener(String screenerId); + + public Optional getScreenerMetaDataOnly(String screenerId); + + public String saveNewScreener(Screener screener) throws Exception; + + public void updateScreener(Screener screener) throws Exception; + + public void deleteScreener(String screenerId) throws Exception; + + public void addDmnDependency(String screenerId, DmnModel dmnModel) throws Exception; + + public void deleteDmnDependency(String screenerId, String groupId, String artifactId, String version) throws Exception; +} diff --git a/builder-api/src/main/java/org/acme/persistence/ScreenerRepositoryImpl.java b/builder-api/src/main/java/org/acme/persistence/ScreenerRepositoryImpl.java new file mode 100644 index 0000000..0ea0c81 --- /dev/null +++ b/builder-api/src/main/java/org/acme/persistence/ScreenerRepositoryImpl.java @@ -0,0 +1,108 @@ +package org.acme.persistence; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.acme.constants.CollectionNames; +import org.acme.constants.FieldNames; +import org.acme.mapper.ScreenerMapper; +import org.acme.model.domain.DmnModel; +import org.acme.model.domain.Screener; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@ApplicationScoped +public class ScreenerRepositoryImpl implements ScreenerRepository { + + @Inject + private StorageService storageService; + + @Override + public List getScreeners(String userId) { + + List> screenersMaps = FirestoreUtils.getFirestoreDocsByField( + CollectionNames.SCREENER_COLLECTION, + FieldNames.OWNER_ID, + userId); + + List screeners = screenersMaps.stream().map(ScreenerMapper::fromMap).toList(); + + return screeners; + } + + @Override + public Optional getScreener(String screenerId){ + Optional> dataOpt = FirestoreUtils.getFirestoreDocById(CollectionNames.SCREENER_COLLECTION, screenerId); + if (dataOpt.isEmpty()){ + return Optional.empty(); + } + Map data = dataOpt.get(); + Screener screener = ScreenerMapper.fromMap(data); + + String formPath = storageService.getScreenerWorkingFormSchemaPath(screenerId); + Map formSchema = storageService.getFormSchemaFromStorage(formPath); + screener.setFormSchema(formSchema); + + String dmnPath = storageService.getScreenerWorkingDmnModelPath(screenerId); + Optional dmnModel = FirestoreUtils.getFileAsStringFromStorage(dmnPath); + dmnModel.ifPresent(screener::setDmnModel); + + return Optional.of(screener); + } + + @Override + public Optional getScreenerMetaDataOnly(String screenerId){ + Optional> dataOpt = FirestoreUtils.getFirestoreDocById(CollectionNames.SCREENER_COLLECTION, screenerId); + if (dataOpt.isEmpty()){ + return Optional.empty(); + } + Map data = dataOpt.get(); + Screener screener = ScreenerMapper.fromMap(data); + + return Optional.of(screener); + } + + + private Boolean doesAttributeExistAndOfType(Map map, String key, Class expectedClass){ + return map.containsKey(key) && expectedClass.isInstance(map.get(key)); + } + + + @Override + public String saveNewScreener(Screener screener) throws Exception{ + Map data = ScreenerMapper.fromScreener(screener); + return FirestoreUtils.persistDocument(CollectionNames.SCREENER_COLLECTION, data); + } + + @Override + public void updateScreener(Screener screener) throws Exception { + Map data = ScreenerMapper.fromScreener(screener); + FirestoreUtils.updateDocument(CollectionNames.SCREENER_COLLECTION, data, screener.getId()); + } + + @Override + public void deleteScreener(String screenerId) throws Exception { + FirestoreUtils.deleteDocument(CollectionNames.SCREENER_COLLECTION, screenerId); + } + + @Override + public void addDmnDependency(String screenerId, DmnModel dmnModel) throws Exception { + Map modelMap = new HashMap<>(); + modelMap.put(FieldNames.GROUP_ID, dmnModel.getGroupId()); + modelMap.put(FieldNames.ARTIFACT_ID, dmnModel.getArtifactId()); + modelMap.put(FieldNames.VERSION, dmnModel.getVersion()); + FirestoreUtils.addObjectToListFieldOfDocument(CollectionNames.SCREENER_COLLECTION, screenerId, FieldNames.DEPENDENCIES, modelMap); + } + + @Override + public void deleteDmnDependency(String screenerId, String groupId, String artifactId, String version) throws Exception{ + Map modelMap = new HashMap<>(); + modelMap.put(FieldNames.GROUP_ID, groupId); + modelMap.put(FieldNames.ARTIFACT_ID, artifactId); + modelMap.put(FieldNames.VERSION, version); + FirestoreUtils.removeObjectFromListFieldOfDocument(CollectionNames.SCREENER_COLLECTION, screenerId, FieldNames.DEPENDENCIES, modelMap); + } +} + diff --git a/builder-api/src/main/java/org/acme/persistence/StorageService.java b/builder-api/src/main/java/org/acme/persistence/StorageService.java new file mode 100644 index 0000000..c7d7c3d --- /dev/null +++ b/builder-api/src/main/java/org/acme/persistence/StorageService.java @@ -0,0 +1,35 @@ +package org.acme.persistence; + +import com.fasterxml.jackson.databind.JsonNode; + +import java.io.InputStream; +import java.util.Map; +import java.util.Optional; + +public interface StorageService { + void writeStringToStorage(String filePath, String content, String contentType); + + void writeBytesToStorage(String filePath, byte[] content, String contentType); + + void writeJsonToStorage(String filePath, JsonNode json); + + Optional getFileInputStreamFromStorage(String filePath); + + Optional getStringFromStorage(String filePath); + + Optional getFileBytesFromStorage(String filePath); + + String getScreenerWorkingDmnModelPath(String screenerId); + + String getScreenerWorkingFormSchemaPath(String screenerId); + + String getScreenerPublishedFormSchemaPath(String screenerId); + + String getPublishedCompiledDmnModelPath(String screenerId); + + String getWorkingCompiledDmnModelPath(String screenerId); + + Map getFormSchemaFromStorage(String filePath); + + void updatePublishedFormSchemaArtifact(String screenerId) throws Exception; +} diff --git a/builder-api/src/main/java/org/acme/service/DmnParser.java b/builder-api/src/main/java/org/acme/service/DmnParser.java new file mode 100644 index 0000000..826950c --- /dev/null +++ b/builder-api/src/main/java/org/acme/service/DmnParser.java @@ -0,0 +1,48 @@ +package org.acme.service; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import com.github.javaparser.utils.Log; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +public class DmnParser { + private String nameSpace; + private String name; + + public DmnParser(String dmnXml) { + + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(new ByteArrayInputStream(dmnXml.getBytes("UTF-8"))); + + // Get the document element (dmn:definitions) + Element root = doc.getDocumentElement(); + + // Extract attributes + String name = root.getAttribute("name"); + String namespace = root.getAttribute("namespace"); + + this.name = name; + this.nameSpace = namespace; + System.out.println("Name: " + name); + System.out.println("Namespace: " + namespace); + + } catch (Exception e) { + Log.error(e); + } + } + + public String getName() { + return name; + } + public String getNameSpace(){ + return nameSpace; + } +} diff --git a/builder-api/src/main/java/org/acme/service/DmnService.java b/builder-api/src/main/java/org/acme/service/DmnService.java new file mode 100644 index 0000000..f07b252 --- /dev/null +++ b/builder-api/src/main/java/org/acme/service/DmnService.java @@ -0,0 +1,10 @@ +package org.acme.service; +import org.acme.model.domain.Screener; +import java.io.IOException; +import java.util.Map; + +public interface DmnService { + public Map evaluateDecision(Screener screener, Map inputs) throws IOException; + public String compilePublishedDmnModel(String screenerId) throws Exception; + public void compileWorkingDmnModel(Screener screener) throws Exception; +} diff --git a/builder-api/src/main/java/org/acme/service/KieDmnService.java b/builder-api/src/main/java/org/acme/service/KieDmnService.java new file mode 100644 index 0000000..4d480c3 --- /dev/null +++ b/builder-api/src/main/java/org/acme/service/KieDmnService.java @@ -0,0 +1,232 @@ +package org.acme.service; + +import io.quarkus.logging.Log; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.NotFoundException; +import org.acme.model.domain.DmnModel; +import org.acme.model.domain.Screener; +import org.acme.model.dto.Dependency; +import org.acme.persistence.DmnModelRepository; +import org.acme.persistence.ScreenerRepository; +import org.acme.persistence.StorageService; +import org.kie.api.KieServices; +import org.kie.api.builder.*; +import org.kie.api.io.Resource; +import org.kie.api.runtime.KieContainer; +import org.kie.api.runtime.KieSession; +import org.kie.dmn.api.core.*; +import java.io.*; +import java.util.*; +import java.util.stream.Collectors; +import org.drools.compiler.kie.builder.impl.InternalKieModule; + +@ApplicationScoped +public class KieDmnService implements DmnService { + + @Inject + private StorageService storageService; + + @Inject + private DmnModelRepository dmnModelRepository; + + @Inject + private ScreenerRepository screenerRepository; + + public Map evaluateDecision(Screener screener, Map inputs) throws IOException{ + + String filePath = storageService.getWorkingCompiledDmnModelPath(screener.getId()); + Optional dmnDataOpt = storageService.getFileBytesFromStorage(filePath); + + if (dmnDataOpt.isEmpty()){ + throw new NotFoundException(); + } + + byte[] dmnModuleData = dmnDataOpt.get(); + + KieSession kieSession = initializeKieSession(dmnModuleData); + DMNRuntime dmnRuntime = kieSession.getKieRuntime(DMNRuntime.class); + + try { + DMNModel dmnModel = dmnRuntime.getModel(screener.getWorkingDmnNameSpace(), screener.getWorkingDmnName()); + + DMNContext context = dmnRuntime.newContext(); + for (String key : inputs.keySet()) { + context.set(key, inputs.get(key)); + } + + DMNResult dmnResult = dmnRuntime.evaluateAll(dmnModel, context); + + Map response = new LinkedHashMap<>(); + response.put("inputs", inputs); + + + List> decisions = new ArrayList<>(); + for (DMNDecisionResult decisionResult : dmnResult.getDecisionResults()) { + Map decisionDetail = new LinkedHashMap<>(); + decisionDetail.put("decisionName", decisionResult.getDecisionName()); + decisionDetail.put("result", decisionResult.getResult()); + decisionDetail.put("status", decisionResult.getEvaluationStatus().toString()); + + decisions.add(decisionDetail); + } + response.put("decisions", decisions); + + if (!dmnResult.getMessages().isEmpty()) { + response.put("messages", dmnResult.getMessages().stream() + .map(DMNMessage::getMessage).collect(Collectors.toList())); + } + + kieSession.dispose(); + return response; + } + catch (Exception e){ + return new HashMap<>(); + } finally{ + if (kieSession != null) { + kieSession.dispose(); + } + } + } + + private KieSession initializeKieSession(byte[] moduleBytes) throws IOException { + KieServices kieServices = KieServices.Factory.get(); + Resource jarResource = kieServices.getResources().newByteArrayResource(moduleBytes); + KieModule kieModule = kieServices.getRepository().addKieModule(jarResource); + + ReleaseId releaseId = kieModule.getReleaseId(); + KieContainer kieContainer = kieServices.newKieContainer(releaseId); + return kieContainer.newKieSession(); + } + + public String compilePublishedDmnModel(String screenerId) throws Exception { + Optional dmnXmlOpt = getWorkingDmnXml(screenerId); + if (dmnXmlOpt.isEmpty()) { + throw new Exception("Working Dmn not found for screener: " + screenerId); + } + Optional screenerOpt = screenerRepository.getScreenerMetaDataOnly(screenerId); + if (screenerOpt.isEmpty()) { + throw new Exception("Screener not found for screener: " + screenerId); + } + + String dmnXml = dmnXmlOpt.get(); + Screener screener = screenerOpt.get(); + + // Get Screener DMN Dependencies + Map dependencies = new HashMap<>(); + for (Dependency dep : screener.getDependencies()){ + String key = dep.groupId + ":" + dep.artifactId + ":" + dep.version; + String xml = getDependencyXml(dep, key); + dependencies.put(key, xml); + } + + byte[] serializedModel = compileDmnModel(dmnXml, dependencies, screenerId); + String filPath = storageService.getPublishedCompiledDmnModelPath(screenerId); + storageService.writeBytesToStorage(filPath, serializedModel, "application/java-archive"); + Log.info("Saved compiled published dmn for model " + screenerId + " to storage."); + return dmnXml; + } + + public void compileWorkingDmnModel(Screener screener) throws Exception { + String dmnXml = screener.getDmnModel(); + if (dmnXml == null) { + throw new Exception("Working Dmn not found for screener: " + screener.getId()); + } + + // Get Screener DMN Dependencies + Map dependencies = new HashMap<>(); + if (screener.getDependencies() == null){ + screener.setDependencies(new ArrayList<>()); + } + for (Dependency dep : screener.getDependencies()){ + String key = dep.groupId + ":" + dep.artifactId + ":" + dep.version; + String xml = getDependencyXml(dep, key); + dependencies.put(key, xml); + } + + byte[] serializedModel = compileDmnModel(dmnXml, dependencies, screener.getId()); + String filPath = storageService.getWorkingCompiledDmnModelPath(screener.getId()); + storageService.writeBytesToStorage(filPath, serializedModel, "application/java-archive"); + Log.info("Saved compiled working dmn for model " + screener.getId() + " to storage."); + } + + private String getDependencyXml(Dependency dep, String key) throws Exception { + Optional model = dmnModelRepository.getDmnModel(dep.groupId, dep.artifactId, dep.version); + + if (model.isEmpty()){ + Log.error("Dmn model not fount: " + key); + throw new Exception("Working Dmn not found for screener"); + } + Optional xmlOpt = storageService.getStringFromStorage(model.get().getStorageLocation()); + + if (xmlOpt.isEmpty()){ + Log.error("Dmn xml not fount: " + key); + throw new Exception("Working Dmn xml not found for screener"); + } + + return xmlOpt.get(); + } + + + private byte[] compileDmnModel(String dmnXml, Map dependenciesMap, String modelId) throws IOException { + Log.info("Compiling and saving DMN model: " + modelId); + + KieServices kieServices = KieServices.Factory.get(); + // 1. Compile the DMN XML into a KieBase + KieFileSystem kfs = kieServices.newKieFileSystem(); + // Use a unique ReleaseId for each compilation if you plan to update models + // For production, consider versioning the ReleaseId carefully. + ReleaseId releaseId = kieServices.newReleaseId("user-model", modelId, "1.0.0"); + kfs.write("src/main/resources/model.dmn", dmnXml); + + + String kmoduleXml = "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + ""; + + kfs.write("src/main/resources/META-INF/kmodule.xml", kmoduleXml); + + // Write all imported DMN models + for (Map.Entry entry : dependenciesMap.entrySet()) { + // Ensure the path starts with "src/main/resources/" to be picked up by KieBuilder + String resourcePath = entry.getKey(); + if (!resourcePath.startsWith("src/main/resources/")) { + resourcePath = "src/main/resources/" + resourcePath + ".dmn"; + } + kfs.write(resourcePath, entry.getValue()); + Log.info("Added imported DMN model to KieFileSystem: " + resourcePath); + } + + kfs.generateAndWritePomXML(releaseId); + + KieBuilder kieBuilder = kieServices.newKieBuilder(kfs); + kieBuilder.buildAll(); + Results results = kieBuilder.getResults(); + + if (results.hasMessages(Message.Level.ERROR)) { + Log.error("DMN Compilation errors for model " + modelId + ":"); + for (Message message : results.getMessages(Message.Level.ERROR)) { + Log.error(message.getText()); + } + throw new IllegalStateException("DMN Model compilation failed for model: " + modelId); + } + + InternalKieModule kieModule = (InternalKieModule) kieBuilder.getKieModule(); + byte[] kieModuleBytes = kieModule.getBytes(); + + Log.info("Serialized kieModule for model " + modelId); + return kieModuleBytes; + } + + private Optional getWorkingDmnXml(String screenerId) { + String filePath = storageService.getScreenerWorkingDmnModelPath(screenerId); + Optional dmnXml = storageService.getStringFromStorage(filePath); + if (dmnXml.isEmpty()){ + throw new RuntimeException("working DMN file not found"); + } + return dmnXml; + } +} diff --git a/builder-api/src/main/java/org/acme/service/ModelLibraryService.java b/builder-api/src/main/java/org/acme/service/ModelLibraryService.java new file mode 100644 index 0000000..a023600 --- /dev/null +++ b/builder-api/src/main/java/org/acme/service/ModelLibraryService.java @@ -0,0 +1,71 @@ +package org.acme.service; + +import io.quarkus.logging.Log; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.acme.mapper.DmnModelMapper; +import org.acme.model.domain.DmnModel; +import org.acme.model.dto.Dependency; +import org.acme.model.dto.DmnModelSummary; +import org.acme.persistence.DmnModelRepository; +import org.acme.persistence.StorageService; + +import java.util.List; +import java.util.Optional; + +@ApplicationScoped +public class ModelLibraryService { + + @Inject + DmnModelRepository dmnModelRepository; + + @Inject + StorageService storageService; + + public Response getDmnModels(){ + + List dmnModels = dmnModelRepository.getAllDmnModels(); + List modelSummaries = dmnModels.stream() + .map(model -> DmnModelMapper.summaryFromDmnModel(model)) + .toList(); + + return Response.ok(modelSummaries, MediaType.APPLICATION_JSON).build(); + } + + public Response getDmnModel(String groupId, String artifactId, String version){ + + if (!isIdValid(groupId) || !isIdValid(artifactId) || !isIdValid(version)){ + return Response.status(Response.Status.BAD_REQUEST).build(); + } + + Optional dmnModelOpt = dmnModelRepository.getDmnModel(groupId, artifactId, version); + + if (dmnModelOpt.isEmpty()){ + return Response.status(Response.Status.NOT_FOUND).build(); + } + + Optional xmlOpt = storageService.getStringFromStorage(dmnModelOpt.get().getStorageLocation()); + + if (xmlOpt.isEmpty()){ + return Response.status(Response.Status.NOT_FOUND).build(); + } + + Dependency dependency = new Dependency(); + dependency.groupId = groupId; + dependency.artifactId = artifactId; + dependency.version = version; + dependency.xml = xmlOpt.get(); + + return Response.ok().entity(dependency).build(); + } + + // validate Ids coming from URL to avoid injection attacks + public boolean isIdValid(String id){ + if (!id.matches("^[a-zA-Z0-9_.-]{3,64}$")) { + return false; + } + return true; + } +} diff --git a/builder-api/src/main/java/org/acme/service/ScreenerDependencyService.java b/builder-api/src/main/java/org/acme/service/ScreenerDependencyService.java new file mode 100644 index 0000000..ee32a0d --- /dev/null +++ b/builder-api/src/main/java/org/acme/service/ScreenerDependencyService.java @@ -0,0 +1,126 @@ +package org.acme.service; + +import io.quarkus.logging.Log; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.core.Response; +import org.acme.model.domain.DmnModel; +import org.acme.model.domain.Screener; +import org.acme.model.dto.DmnImportRequest; +import org.acme.persistence.DmnModelRepository; +import org.acme.persistence.ScreenerRepository; + +import java.util.Map; +import java.util.Optional; + +@ApplicationScoped +public class ScreenerDependencyService { + + @Inject + ScreenerRepository screenerRepository; + + @Inject + DmnModelRepository dmnModelRepository; + + public Response addDependency(DmnImportRequest request, String userId) { + String screenerId = request.screenerId; + String groupId = request.groupId; + String artifactId = request.artifactId; + String version = request.version; + + if (screenerId == null || screenerId.isBlank() || + groupId == null || groupId.isBlank() || + artifactId == null || artifactId.isBlank() || + version == null || version.isBlank()) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("Error: Missing required data.") + .build(); + } + + if (!isUserAuthorizedToAccessScreener(userId, screenerId)) { + return Response.status(Response.Status.UNAUTHORIZED).build(); + } + + try { + Optional modelOpt = dmnModelRepository.getDmnModel(groupId, artifactId, version); + + if (modelOpt.isEmpty()) { + return Response.status(Response.Status.NOT_FOUND) + .entity(Map.of("error", "The DMN Model you are trying to import is not found.")) + .build(); + } + + screenerRepository.addDmnDependency(screenerId, modelOpt.get()); + + return Response.ok().build(); + + } catch (Exception e) { + Log.error("Failed to add DMN dependency to screener: " + screenerId, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + } + + + public Response deleteDependency(DmnImportRequest request, String userId) { + String screenerId = request.screenerId; + String groupId = request.groupId; + String artifactId = request.artifactId; + String version = request.version; + + if (screenerId == null || screenerId.isBlank() || + groupId == null || groupId.isBlank() || + artifactId == null || artifactId.isBlank() || + version == null || version.isBlank()) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("Error: Missing required data.") + .build(); + } + + if (!isUserAuthorizedToAccessScreener(userId, screenerId)) { + return Response.status(Response.Status.UNAUTHORIZED).build(); + } + + try { + screenerRepository.deleteDmnDependency(screenerId, groupId, artifactId, version); + return Response.ok().build(); + + } catch (Exception e) { + Log.error("Failed to delete DMN dependency from screener: " + screenerId, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + } + + public Response getScreenerDependencies(String screenerId, String userId){ + if (!isIdValid(screenerId)){ + return Response.status(Response.Status.BAD_REQUEST).build(); + } + + if (!isUserAuthorizedToAccessScreener(userId, screenerId)) { + return Response.status(Response.Status.UNAUTHORIZED).build(); + } + + return Response.status(Response.Status.OK).build(); + } + + private boolean isUserAuthorizedToAccessScreener(String userId, String screenerId) { + Optional screenerOptional = screenerRepository.getScreenerMetaDataOnly(screenerId); + if (screenerOptional.isEmpty()){ + return false; + } + Screener screener = screenerOptional.get(); + String ownerId = screener.getOwnerId(); + if (userId.equals(ownerId)){ + return true; + } + return false; + } + + // validate Ids coming from URL to avoid injection attacks + public boolean isIdValid(String id){ + if (!id.matches("^[a-zA-Z0-9_-]{5,64}$")) { + return false; + } + return true; + } + +} diff --git a/builder-api/src/main/resources/application.properties b/builder-api/src/main/resources/application.properties new file mode 100644 index 0000000..c105477 --- /dev/null +++ b/builder-api/src/main/resources/application.properties @@ -0,0 +1,8 @@ +quarkus.http.cors.enabled=true +quarkus.http.cors.origins=* +quarkus.http.cors.methods=GET,POST,PUT,DELETE +quarkus.http.cors.headers=Authorization,Content-Type + +quarkus.datasource.db-kind=sqlite +quarkus.datasource.jdbc.url=jdbc:sqlite::resource:data/locations.db +quarkus.datasource.jdbc.driver=org.sqlite.JDBC \ No newline at end of file diff --git a/src/main/resources/data/locations.db b/builder-api/src/main/resources/data/locations.db similarity index 100% rename from src/main/resources/data/locations.db rename to builder-api/src/main/resources/data/locations.db diff --git a/builder-api/src/test/java/org/acme/controller/ScreenerResourceTest.java b/builder-api/src/test/java/org/acme/controller/ScreenerResourceTest.java new file mode 100644 index 0000000..6ba026f --- /dev/null +++ b/builder-api/src/test/java/org/acme/controller/ScreenerResourceTest.java @@ -0,0 +1,94 @@ +package org.acme.controller; +import jakarta.ws.rs.core.Response; +import org.acme.model.domain.Screener; +import org.acme.model.dto.DmnImportRequest; +import org.acme.persistence.ScreenerRepository; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import org.acme.service.ScreenerDependencyService; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class ScreenerResourceTest { + + @Mock + ScreenerRepository screenerRepository; + + @InjectMocks + ScreenerDependencyService importService = new ScreenerDependencyService(); + + private final String TEST_USER_ID = "TEST_USER_ID"; + private final String TEST_SCREENER_ID = "TEST_SCREENER_ID"; + + @Test + void test_whenMissingScreenerId_addImport_returnsStatus400(){ + DmnImportRequest request = getTestRequest(); + request.screenerId = null; + Response response = importService.addDependency(request, TEST_USER_ID); + assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus()); + } + + @Test + void test_whenMissingGroupId_addImport_returnsStatus400(){ + DmnImportRequest request = getTestRequest(); + request.groupId = null; + Response response = importService.addDependency(request, TEST_USER_ID); + assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus()); + } + + @Test + void test_whenMissingArtifactId_addImport_returnsStatus400(){ + DmnImportRequest request = getTestRequest(); + request.artifactId = null; + Response response = importService.addDependency(request, TEST_USER_ID); + assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus()); + } + + @Test + void test_whenMissingVersion_addImport_returnsStatus400(){ + DmnImportRequest request = getTestRequest(); + request.version = null; + Response response = importService.addDependency(request, TEST_USER_ID); + assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus()); + } + + @Test + void test_whenMissingUserId_addImport_resturnsStatus401(){ + DmnImportRequest request = getTestRequest(); + Response response = importService.addDependency(request, null); + assertEquals(Response.Status.UNAUTHORIZED.getStatusCode(), response.getStatus()); + } + + @Test + void test(){ + DmnImportRequest request = getTestRequest(); + Screener screener = getTestScreener(); + when(screenerRepository.getScreenerMetaDataOnly(eq(TEST_SCREENER_ID))).thenReturn(Optional.of(screener)); + Response response = importService.addDependency(request, TEST_USER_ID); + assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + } + + private DmnImportRequest getTestRequest(){ + DmnImportRequest request = new DmnImportRequest(); + request.screenerId = "TEST_SCREENER_ID"; + request.groupId = "TEST_GROUP_ID"; + request.artifactId = "TEST_ARTIFACT_ID"; + request.version = "TEST_VERSION"; + return request; + } + + private Screener getTestScreener(){ + Screener screener = new Screener(); + screener.setOwnerId(TEST_USER_ID); + return screener; + } +} diff --git a/builder-api/src/test/java/org/acme/service/DmnParserTest.java b/builder-api/src/test/java/org/acme/service/DmnParserTest.java new file mode 100644 index 0000000..94c26b6 --- /dev/null +++ b/builder-api/src/test/java/org/acme/service/DmnParserTest.java @@ -0,0 +1,116 @@ +package org.acme.service; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class DmnParserTest { + + private String testXml = """ + + + + + + + + + + + +householdCount + + + + + + + >4 + + + "Eligable" + + + + + + + + <=4 + + + "Not eligable" + + + + + + + + + + + + + + + + + 50 +100 +100 +100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """; + + @Test + public void testParseDmnName(){ + DmnParser dmnParser = new DmnParser(testXml); + assertEquals(dmnParser.getName(), "TEST_NAME"); + } + + @Test + public void testParseDmnNameSpace(){ + DmnParser dmnParser = new DmnParser(testXml); + assertEquals(dmnParser.getNameSpace(), "TEST_NAMESPACE"); + } + + @Test + public void testParseDmnNameNullInputReturnsNull(){ + DmnParser dmnParser = new DmnParser(null); + assertNull(dmnParser.getName()); + } + + + @Test + public void testParseDmnNameSpaceNullInputReturnsNull(){ + DmnParser dmnParser = new DmnParser(null); + assertNull(dmnParser.getNameSpace()); + } +} \ No newline at end of file diff --git a/builder-frontend/.gitignore b/builder-frontend/.gitignore new file mode 100644 index 0000000..1cac559 --- /dev/null +++ b/builder-frontend/.gitignore @@ -0,0 +1,25 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +.env \ No newline at end of file diff --git a/builder-frontend/README.md b/builder-frontend/README.md new file mode 100644 index 0000000..167c567 --- /dev/null +++ b/builder-frontend/README.md @@ -0,0 +1,28 @@ +## Usage + +```bash +$ npm install # or pnpm install or yarn install +``` + +### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs) + +## Available Scripts + +In the project directory, you can run: + +### `npm run dev` + +Runs the app in the development mode.
+Open [http://localhost:5173](http://localhost:5173) to view it in the browser. + +### `npm run build` + +Builds the app for production to the `dist` folder.
+It correctly bundles Solid in production mode and optimizes the build for the best performance. + +The build is minified and the filenames include the hashes.
+Your app is ready to be deployed! + +## Deployment + +Learn more about deploying your application with the [documentations](https://vite.dev/guide/static-deploy.html) diff --git a/builder-frontend/index.html b/builder-frontend/index.html new file mode 100644 index 0000000..5cba992 --- /dev/null +++ b/builder-frontend/index.html @@ -0,0 +1,19 @@ + + + + + + + + Vite + Solid + + +
+ + + diff --git a/builder-frontend/package-lock.json b/builder-frontend/package-lock.json new file mode 100644 index 0000000..a2348ce --- /dev/null +++ b/builder-frontend/package-lock.json @@ -0,0 +1,5506 @@ +{ + "name": "builder-frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "builder-frontend", + "version": "0.0.0", + "dependencies": { + "@bpmn-io/form-js": "^1.15.2", + "@bpmn-io/form-js-editor": "^1.15.2", + "@kie-tools-core/editor": "^10.0.0", + "@kogito-tooling/kie-editors-standalone": "^0.16.0", + "@solidjs/router": "^0.15.3", + "dmn-js": "^17.2.1", + "firebase": "^11.9.1", + "lodash.debounce": "^4.0.8", + "solid-bootstrap": "^1.0.21", + "solid-js": "^1.9.5" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.1.8", + "autoprefixer": "^10.4.21", + "postcss": "^8.5.4", + "sass-embedded": "^1.89.2", + "tailwindcss": "^4.1.8", + "vite": "^6.3.5", + "vite-plugin-solid": "^2.11.6" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.3.tgz", + "integrity": "sha512-V42wFfx1ymFte+ecf6iXghnnP8kWTO+ZLXIyZq+1LAXHHvTZdVxicn4yiVYdYMGaCO3tmqub11AorKkv+iodqw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz", + "integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.4", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.27.4", + "@babel/types": "^7.27.3", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.3.tgz", + "integrity": "sha512-xnlJYj5zepml8NXtjkG0WquFUv8RskFqyFcVgTBp5k+NaA/8uw/K+OSVf8AMGw5e9HKP2ETd5xpK5MLZQD6b4Q==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.27.3", + "@babel/types": "^7.27.3", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.4.tgz", + "integrity": "sha512-Y+bO6U+I7ZKaM5G5rDUZiYfUvQPUibYmAFe7EnKdnKBbVXDZxvp+MWOH5gYciY0EPk4EScsuFMQBbEfpdRKSCQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.4.tgz", + "integrity": "sha512-BRmLHGwpUqLFR2jzx9orBuX/ABDkj2jLKOXrHDTN2aOKL+jFDDKaRNo9nyYsIl9h/UE/7lMKdDjKQQyxKKDZ7g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.3" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.4.tgz", + "integrity": "sha512-t3yaEOuGu9NlIZ+hIeGbBjFtZT7j2cb2tg0fuaJKeGotchRjjLfrBA9Kwf8quhpP1EUuxModQg04q/mBwyg8uA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", + "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz", + "integrity": "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bpmn-io/cm-theme": { + "version": "0.1.0-alpha.2", + "resolved": "https://registry.npmjs.org/@bpmn-io/cm-theme/-/cm-theme-0.1.0-alpha.2.tgz", + "integrity": "sha512-ZILgiYzxk3KMvxplUXmdRFQo45/JehDPg5k9tWfehmzUOSE13ssyLPil8uCloMQnb3yyzyOWTjb/wzKXTHlFQw==", + "dependencies": { + "@codemirror/language": "^6.3.1", + "@codemirror/view": "^6.5.1", + "@lezer/highlight": "^1.1.4" + } + }, + "node_modules/@bpmn-io/diagram-js-ui": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bpmn-io/diagram-js-ui/-/diagram-js-ui-0.2.3.tgz", + "integrity": "sha512-OGyjZKvGK8tHSZ0l7RfeKhilGoOGtFDcoqSGYkX0uhFlo99OVZ9Jn1K7TJGzcE9BdKwvA5Y5kGqHEhdTxHvFfw==", + "dependencies": { + "htm": "^3.1.1", + "preact": "^10.11.2" + } + }, + "node_modules/@bpmn-io/dmn-variable-resolver": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@bpmn-io/dmn-variable-resolver/-/dmn-variable-resolver-0.7.0.tgz", + "integrity": "sha512-ssL8fch5U0q8efbrHdgSGznh5Dlk+R0MNnazCtDpyac6yqlgn/JQ/HewCjacTk4XFubbMiDbuEIESHLBsWCBxg==" + }, + "node_modules/@bpmn-io/draggle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@bpmn-io/draggle/-/draggle-4.1.1.tgz", + "integrity": "sha512-2frw1gBl5I3XGrIDg4CBy6bpJiOuslKUOg9T91Fke6bIttFkF0zxlTKh4E4zU8g7gAo4ze0HnKMZDgHxea+Itw==", + "dependencies": { + "contra": "^1.9.4" + } + }, + "node_modules/@bpmn-io/feel-editor": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@bpmn-io/feel-editor/-/feel-editor-1.10.1.tgz", + "integrity": "sha512-Kq3TFKaCovDchb++WwS8yUvIke947pjW7k4Cu0zJXPnoMBbsbzMPXUDvtrHRP7/1D40wEqBAjIpElugFdpQM4g==", + "dependencies": { + "@bpmn-io/feel-lint": "^1.4.0", + "@codemirror/autocomplete": "^6.16.2", + "@codemirror/commands": "^6.8.0", + "@codemirror/language": "^6.10.2", + "@codemirror/lint": "^6.8.4", + "@codemirror/state": "^6.5.1", + "@codemirror/view": "^6.36.2", + "@lezer/highlight": "^1.2.1", + "lang-feel": "^2.3.0", + "min-dom": "^4.2.1" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/@bpmn-io/feel-lint": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@bpmn-io/feel-lint/-/feel-lint-1.4.0.tgz", + "integrity": "sha512-1bsdR/9vPD7RQVqWWPk0X0tpjLsYTDrCxIzOVtN/h32o4nPGl0dZBU5m07qaFUGD4wG3eOH4Qim1wexHG8YkBw==", + "dependencies": { + "@codemirror/language": "^6.10.8", + "lezer-feel": "^1.7.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@bpmn-io/form-js": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@bpmn-io/form-js/-/form-js-1.15.2.tgz", + "integrity": "sha512-aiGgzSYuG85AtYfXxJUYfeRpSvDO7FPyZk9lstcFja0jJirDuZC/xlQfH4Ywp9wXNg2kmeXjPBj3Nny+92x6hQ==", + "dependencies": { + "@bpmn-io/form-js-carbon-styles": "^1.15.2", + "@bpmn-io/form-js-editor": "^1.15.2", + "@bpmn-io/form-js-playground": "^1.15.2", + "@bpmn-io/form-js-viewer": "^1.15.2" + } + }, + "node_modules/@bpmn-io/form-js-carbon-styles": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@bpmn-io/form-js-carbon-styles/-/form-js-carbon-styles-1.15.2.tgz", + "integrity": "sha512-QUpJA2hXs8SMmiDtKXRSajEZosPpX4DKBLKPGS6litiPTgJ7EmhoNtGnz3WWsJL4SZR9JgZ/ObYNMzuDAESbng==" + }, + "node_modules/@bpmn-io/form-js-editor": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@bpmn-io/form-js-editor/-/form-js-editor-1.15.2.tgz", + "integrity": "sha512-iomcTAkXxUy8X4ARUwSNzFRP0XEfyj4PCdKrNgR3gnUygcRufi/rS/BFj+8mRGKUdCGV+mDzEqshDX0mL0zLgQ==", + "dependencies": { + "@bpmn-io/draggle": "^4.1.1", + "@bpmn-io/form-js-viewer": "^1.15.2", + "@bpmn-io/properties-panel": "^3.26.3", + "array-move": "^4.0.0", + "big.js": "^6.2.2", + "ids": "^1.0.5", + "min-dash": "^4.2.3", + "min-dom": "^4.1.0", + "preact": "^10.5.14" + } + }, + "node_modules/@bpmn-io/form-js-playground": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@bpmn-io/form-js-playground/-/form-js-playground-1.15.2.tgz", + "integrity": "sha512-lV9fYL1v8hyfx5MeY7Npk2hL124AkQzld52eUE7RYPPqr0OSxaDqCTNM1y3awUdhDnc97fDn45tQoiJw1LcoRA==", + "dependencies": { + "@bpmn-io/form-js-editor": "^1.15.2", + "@bpmn-io/form-js-viewer": "^1.15.2", + "@codemirror/autocomplete": "^6.18.6", + "@codemirror/commands": "^6.8.0", + "@codemirror/lang-json": "^6.0.1", + "@codemirror/language": "^6.11.0", + "@codemirror/lint": "^6.8.4", + "@codemirror/state": "^6.5.2", + "@codemirror/view": "^6.36.4", + "classnames": "^2.5.1", + "codemirror": "^6.0.1", + "downloadjs": "^1.4.7", + "file-drops": "^0.5.0", + "mitt": "^3.0.1", + "preact": "^10.5.14" + } + }, + "node_modules/@bpmn-io/form-js-viewer": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@bpmn-io/form-js-viewer/-/form-js-viewer-1.15.2.tgz", + "integrity": "sha512-Es5cl+F8WokhpNAsn45uYT9PPVl+iODNW9TugP6o2atsOZf+dz2JvPHPVGSZ1g5K6ZPRqg4xQcpzdsR7w4RJKA==", + "dependencies": { + "@carbon/grid": "^11.32.2", + "big.js": "^6.2.2", + "classnames": "^2.5.1", + "didi": "^10.2.2", + "dompurify": "^3.2.4", + "feelers": "^1.4.0", + "feelin": "^4.3.0", + "flatpickr": "^4.6.13", + "ids": "^1.0.5", + "lodash": "^4.17.21", + "luxon": "^3.5.0", + "marked": "^15.0.7", + "min-dash": "^4.2.3", + "preact": "^10.5.14" + } + }, + "node_modules/@bpmn-io/properties-panel": { + "version": "3.27.3", + "resolved": "https://registry.npmjs.org/@bpmn-io/properties-panel/-/properties-panel-3.27.3.tgz", + "integrity": "sha512-uGHdm63C/l97pEIKEcgJs8cusBxvjdGWQUwEgpgIIVKZpXtXJ7EyT5BV76+OoRby9MTZIoRRcHoTGTdm7U0lDA==", + "dependencies": { + "@bpmn-io/feel-editor": "^1.10.1", + "@codemirror/view": "^6.28.1", + "classnames": "^2.3.1", + "feelers": "^1.4.0", + "focus-trap": "^7.5.2", + "min-dash": "^4.1.1", + "min-dom": "^4.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@bufbuild/protobuf": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.5.2.tgz", + "integrity": "sha512-foZ7qr0IsUBjzWIq+SuBLfdQCpJ1j8cTuNNT4owngTHoN5KsJb8L9t65fzz7SCeSWzescoOil/0ldqiL041ABg==", + "dev": true + }, + "node_modules/@carbon/grid": { + "version": "11.36.0", + "resolved": "https://registry.npmjs.org/@carbon/grid/-/grid-11.36.0.tgz", + "integrity": "sha512-wYSHLnTw0rCjumBm/OB8H8H/rJR0MJpvBCnBtK6BCPh/vBTnpFX05MgzskZISkoAUomLbExbI6PtxfT5ZmgDMg==", + "hasInstallScript": true, + "dependencies": { + "@carbon/layout": "^11.34.0", + "@ibm/telemetry-js": "^1.5.0" + } + }, + "node_modules/@carbon/layout": { + "version": "11.34.0", + "resolved": "https://registry.npmjs.org/@carbon/layout/-/layout-11.34.0.tgz", + "integrity": "sha512-6PMofb/Ul/CZkP1XivszK2teh0odPV/bUG/PPQlVX1xnFdc+4O9o08JnFG9N2Ie6syncOXRCf8z1CegpV28x9A==", + "hasInstallScript": true, + "dependencies": { + "@ibm/telemetry-js": "^1.5.0" + } + }, + "node_modules/@codemirror/autocomplete": { + "version": "6.18.6", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz", + "integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz", + "integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-json": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz", + "integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/json": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.0.tgz", + "integrity": "sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.8.5", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz", + "integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.35.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.5.11", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz", + "integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", + "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.37.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.37.1.tgz", + "integrity": "sha512-Qy4CAUwngy/VQkEz0XzMKVRcckQuqLYWKqVpDDDghBe5FSXSqfVrJn49nw3ePZHxRUz4nRmb05Lgi+9csWo4eg==", + "dependencies": { + "@codemirror/state": "^6.5.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", + "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", + "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", + "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", + "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", + "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", + "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", + "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", + "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", + "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", + "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", + "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", + "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", + "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", + "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", + "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", + "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", + "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", + "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", + "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", + "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", + "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", + "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", + "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", + "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", + "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@firebase/ai": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@firebase/ai/-/ai-1.4.0.tgz", + "integrity": "sha512-wvF33gtU6TXb6Co8TEC1pcl4dnVstYmRE/vs9XjUGE7he7Sgf5TqSu+EoXk/fuzhw5tKr1LC5eG9KdYFM+eosw==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/analytics": { + "version": "0.10.16", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.16.tgz", + "integrity": "sha512-cMtp19He7Fd6uaj/nDEul+8JwvJsN8aRSJyuA1QN3QrKvfDDp+efjVurJO61sJpkVftw9O9nNMdhFbRcTmTfRQ==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/installations": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.22", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.22.tgz", + "integrity": "sha512-VogWHgwkdYhjWKh8O1XU04uPrRaiDihkWvE/EMMmtWtaUtVALnpLnUurc3QtSKdPnvTz5uaIGKlW84DGtSPFbw==", + "dependencies": { + "@firebase/analytics": "0.10.16", + "@firebase/analytics-types": "0.8.3", + "@firebase/component": "0.6.17", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.3.tgz", + "integrity": "sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg==" + }, + "node_modules/@firebase/app": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.13.1.tgz", + "integrity": "sha512-0O33PKrXLoIWkoOO5ByFaLjZehBctSYWnb+xJkIdx2SKP/K9l1UPFXPwASyrOIqyY3ws+7orF/1j7wI5EKzPYQ==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.10.0.tgz", + "integrity": "sha512-AZlRlVWKcu8BH4Yf8B5EI8sOi2UNGTS8oMuthV45tbt6OVUTSQwFPIEboZzhNJNKY+fPsg7hH8vixUWFZ3lrhw==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.25.tgz", + "integrity": "sha512-3zrsPZWAKfV7DVC20T2dgfjzjtQnSJS65OfMOiddMUtJL1S5i0nAZKsdX0bOEvvrd0SBIL8jYnfpfDeQRnhV3w==", + "dependencies": { + "@firebase/app-check": "0.10.0", + "@firebase/app-check-types": "0.5.3", + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", + "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.3.tgz", + "integrity": "sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng==" + }, + "node_modules/@firebase/app-compat": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.4.1.tgz", + "integrity": "sha512-9VGjnY23Gc1XryoF/ABWtZVJYnaPOnjHM7dsqq9YALgKRtxI1FryvELUVkDaEIUf4In2bfkb9ZENF1S9M273Dw==", + "dependencies": { + "@firebase/app": "0.13.1", + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==" + }, + "node_modules/@firebase/auth": { + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.10.7.tgz", + "integrity": "sha512-77o0aBKCfchdL1gkahARdawHyYefh+wRYn7o60tbwW6bfJNq2idbrRb3WSYCT4yBKWL0+9kKdwxBHPZ6DEiB+g==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.5.27", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.27.tgz", + "integrity": "sha512-axZx/MgjNO7uPA8/nMQiuVotGCngUFMppt5w0pxFIoIPD0kac0bsFdSEh5S2ttuEE0Aq1iUB6Flzwn+wvMgXnQ==", + "dependencies": { + "@firebase/auth": "1.10.7", + "@firebase/auth-types": "0.13.0", + "@firebase/component": "0.6.17", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", + "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==" + }, + "node_modules/@firebase/auth-types": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.13.0.tgz", + "integrity": "sha512-S/PuIjni0AQRLF+l9ck0YpsMOdE8GO2KU6ubmBB7P+7TJUCQDa3R1dlgYm9UzGbbePMZsp0xzB93f2b/CgxMOg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.6.17", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.17.tgz", + "integrity": "sha512-M6DOg7OySrKEFS8kxA3MU5/xc37fiOpKPMz6cTsMUcsuKB6CiZxxNAvgFta8HGRgEpZbi8WjGIj6Uf+TpOhyzg==", + "dependencies": { + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/data-connect": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.3.9.tgz", + "integrity": "sha512-B5tGEh5uQrQeH0i7RvlU8kbZrKOJUmoyxVIX4zLA8qQJIN6A7D+kfBlGXtSwbPdrvyaejcRPcbOtqsDQ9HPJKw==", + "dependencies": { + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.19.tgz", + "integrity": "sha512-khE+MIYK+XlIndVn/7mAQ9F1fwG5JHrGKaG72hblCC6JAlUBDd3SirICH6SMCf2PQ0iYkruTECth+cRhauacyQ==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.10.tgz", + "integrity": "sha512-3sjl6oGaDDYJw/Ny0E5bO6v+KM3KoD4Qo/sAfHGdRFmcJ4QnfxOX9RbG9+ce/evI3m64mkPr24LlmTDduqMpog==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/database": "1.0.19", + "@firebase/database-types": "1.0.14", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.14.tgz", + "integrity": "sha512-8a0Q1GrxM0akgF0RiQHliinhmZd+UQPrxEmUv7MnQBYfVFiLtKOgs3g6ghRt/WEGJHyQNslZ+0PocIwNfoDwKw==", + "dependencies": { + "@firebase/app-types": "0.9.3", + "@firebase/util": "1.12.0" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.7.17", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.17.tgz", + "integrity": "sha512-YhXWA7HlSnekExhZ5u4i0e+kpPxsh/qMrzeNDgsAva71JXK8OOuOx+yLyYBFhmu3Hr5JJDO2fsZA/wrWoQYHDg==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "@firebase/webchannel-wrapper": "1.0.3", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.3.52", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.52.tgz", + "integrity": "sha512-nzt3Sag+EBdm1Jkw/FnnKBPk0LpUUxOlMHMADPBXYhhXrLszxn1+vb64nJsbgRIHfsCn+rg8gyGrb+8frzXrjg==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/firestore": "4.7.17", + "@firebase/firestore-types": "3.0.3", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.3.tgz", + "integrity": "sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.12.8", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.12.8.tgz", + "integrity": "sha512-p+ft6dQW0CJ3BLLxeDb5Hwk9ARw01kHTZjLqiUdPRzycR6w7Z75ThkegNmL6gCss3S0JEpldgvehgZ3kHybVhA==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.17", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.25.tgz", + "integrity": "sha512-V0JKUw5W/7aznXf9BQ8LIYHCX6zVCM8Hdw7XUQ/LU1Y9TVP8WKRCnPB/qdPJ0xGjWWn7fhtwIYbgEw/syH4yTQ==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/functions": "0.12.8", + "@firebase/functions-types": "0.6.3", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.3.tgz", + "integrity": "sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg==" + }, + "node_modules/@firebase/installations": { + "version": "0.6.17", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.17.tgz", + "integrity": "sha512-zfhqCNJZRe12KyADtRrtOj+SeSbD1H/K8J24oQAJVv/u02eQajEGlhZtcx9Qk7vhGWF5z9dvIygVDYqLL4o1XQ==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/util": "1.12.0", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.17.tgz", + "integrity": "sha512-J7afeCXB7yq25FrrJAgbx8mn1nG1lZEubOLvYgG7ZHvyoOCK00sis5rj7TgDrLYJgdj/SJiGaO1BD3BAp55TeA==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/installations": "0.6.17", + "@firebase/installations-types": "0.5.3", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.3.tgz", + "integrity": "sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA==", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/logger": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz", + "integrity": "sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.12.21", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.21.tgz", + "integrity": "sha512-bYJ2Evj167Z+lJ1ach6UglXz5dUKY1zrJZd15GagBUJSR7d9KfiM1W8dsyL0lDxcmhmA/sLaBYAAhF1uilwN0g==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/installations": "0.6.17", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.12.0", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.21", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.21.tgz", + "integrity": "sha512-1yMne+4BGLbHbtyu/VyXWcLiefUE1+K3ZGfVTyKM4BH4ZwDFRGoWUGhhx+tKRX4Tu9z7+8JN67SjnwacyNWK5g==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/messaging": "0.12.21", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.3.tgz", + "integrity": "sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q==" + }, + "node_modules/@firebase/performance": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.6.tgz", + "integrity": "sha512-AsOz74dSTlyQGlnnbLWXiHFAsrxhpssPOsFFi4HgOJ5DjzkK7ZdZ/E9uMPrwFoXJyMVoybGRuqsL/wkIbFITsA==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/installations": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0", + "web-vitals": "^4.2.4" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.19", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.19.tgz", + "integrity": "sha512-4cU0T0BJ+LZK/E/UwFcvpBCVdkStgBMQwBztM9fJPT6udrEUk3ugF5/HT+E2Z22FCXtIaXDukJbYkE/c3c6IHw==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/performance": "0.7.6", + "@firebase/performance-types": "0.2.3", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.3.tgz", + "integrity": "sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ==" + }, + "node_modules/@firebase/remote-config": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.6.4.tgz", + "integrity": "sha512-ZyLJRT46wtycyz2+opEkGaoFUOqRQjt/0NX1WfUISOMCI/PuVoyDjqGpq24uK+e8D5NknyTpiXCVq5dowhScmg==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/installations": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.17.tgz", + "integrity": "sha512-KelsBD0sXSC0u3esr/r6sJYGRN6pzn3bYuI/6pTvvmZbjBlxQkRabHAVH6d+YhLcjUXKIAYIjZszczd1QJtOyA==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/remote-config": "0.6.4", + "@firebase/remote-config-types": "0.4.0", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.4.0.tgz", + "integrity": "sha512-7p3mRE/ldCNYt8fmWMQ/MSGRmXYlJ15Rvs9Rk17t8p0WwZDbeK7eRmoI1tvCPaDzn9Oqh+yD6Lw+sGLsLg4kKg==" + }, + "node_modules/@firebase/storage": { + "version": "0.13.13", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.13.tgz", + "integrity": "sha512-E+MTNcBgpoAynicgVb2ZsHCuEOO4aAiUX5ahNwe/1dEyZpo2H4DwFqKQRNK/sdAIgBbjBwcfV2p0MdPFGIR0Ew==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.23.tgz", + "integrity": "sha512-B/ufkT/R/tSvc2av+vP6ZYybGn26FwB9YVDYg/6Bro+5TN3VEkCeNmfnX3XLa2DSdXUTZAdWCbMxW0povGa4MA==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/storage": "0.13.13", + "@firebase/storage-types": "0.8.3", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.3.tgz", + "integrity": "sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/util": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.12.0.tgz", + "integrity": "sha512-Z4rK23xBCwgKDqmzGVMef+Vb4xso2j5Q8OG0vVL4m4fA5ZjPMYQazu8OJJC3vtQRC3SQ/Pgx/6TPNVsCd70QRw==", + "hasInstallScript": true, + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.3.tgz", + "integrity": "sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==" + }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.15", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", + "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@ibm/telemetry-js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@ibm/telemetry-js/-/telemetry-js-1.9.1.tgz", + "integrity": "sha512-qq8RPafUJHUQieXVCte1kbJEx6JctWzbA/YkXzopbfzIDRT2+hbR9QmgH+KH7bDDNRcDbdHWvHfwJKzThlMtPg==", + "bin": { + "ibmtelemetry": "dist/collect.js" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@kie-tools-core/backend": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@kie-tools-core/backend/-/backend-10.0.0.tgz", + "integrity": "sha512-Ek9xd+kcsq2DNXqIREofKYo1dbwGDVB1CWxNx6xAnoj0IVPzo9WttvkJ462u6cic5xzWoMWHWY7+1UcV1unX9w==", + "dependencies": { + "@kie-tools-core/i18n": "10.0.0", + "@kie-tools-core/notifications": "10.0.0", + "@kie-tools-core/workspace": "10.0.0", + "axios": "^1.6.8", + "fast-xml-parser": "^4.3.1", + "portfinder": "^1.0.32", + "semver": "^7.5.4", + "sinon": "^11.1.1" + } + }, + "node_modules/@kie-tools-core/backend/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@kie-tools-core/editor": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@kie-tools-core/editor/-/editor-10.0.0.tgz", + "integrity": "sha512-6NfsoySb0ey7S9SxJBiAtyifxUhtx1bJtV7e5cXth5UN4ztBmxZc18198iwwx2H5sqy6goPPo2NvNRWnmVjVrg==", + "dependencies": { + "@kie-tools-core/backend": "10.0.0", + "@kie-tools-core/envelope": "10.0.0", + "@kie-tools-core/envelope-bus": "10.0.0", + "@kie-tools-core/i18n": "10.0.0", + "@kie-tools-core/keyboard-shortcuts": "10.0.0", + "@kie-tools-core/notifications": "10.0.0", + "@kie-tools-core/operating-system": "10.0.0", + "@kie-tools-core/patternfly-base": "10.0.0", + "@kie-tools-core/workspace": "10.0.0", + "@patternfly/react-core": "^4.276.6", + "@patternfly/react-icons": "^4.93.6", + "csstype": "^3.0.11", + "minimatch": "^3.0.5", + "react": "^17.0.2", + "react-dom": "^17.0.2" + } + }, + "node_modules/@kie-tools-core/envelope": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@kie-tools-core/envelope/-/envelope-10.0.0.tgz", + "integrity": "sha512-/XB8Tz7Gkr4kkp3AI7I7WPUJUzrUj7LDUamv7eBa+pCKpvWNU8Vl/pahK/J9P7l8r94++gVHGJS5mdpcOzti3A==", + "dependencies": { + "@kie-tools-core/envelope-bus": "10.0.0", + "csstype": "^3.0.11", + "react": "^17.0.2", + "react-dom": "^17.0.2" + } + }, + "node_modules/@kie-tools-core/envelope-bus": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@kie-tools-core/envelope-bus/-/envelope-bus-10.0.0.tgz", + "integrity": "sha512-fePS5u5V75fZv/GmSXJXBLFfnYL+DkVU3YKnz/LCxGp4YFbe9oZx52GKHQ5SaFYc27CK9nIzTmuOQrjN9qdniw==", + "dependencies": { + "react": "^17.0.2", + "react-dom": "^17.0.2" + } + }, + "node_modules/@kie-tools-core/i18n": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@kie-tools-core/i18n/-/i18n-10.0.0.tgz", + "integrity": "sha512-sae8fNNOTlCBq2jprEfOkYgqXo3iu3tGkR20H3nRwJdk415eAuGFafPIAT0viOYOgT6Y+cKxPx6LZv/2Vu/ezA==", + "dependencies": { + "react": "^17.0.2", + "react-dom": "^17.0.2" + } + }, + "node_modules/@kie-tools-core/keyboard-shortcuts": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@kie-tools-core/keyboard-shortcuts/-/keyboard-shortcuts-10.0.0.tgz", + "integrity": "sha512-dV5inKn0V73qO+6Oy/msWoMTwtcz3QKBwatGarVQTm56lXwHJJRs5jubqmQz6edweo0ETBVFu5sLHB5NFMxl3g==", + "dependencies": { + "@kie-tools-core/envelope-bus": "10.0.0", + "@kie-tools-core/operating-system": "10.0.0", + "react": "^17.0.2" + } + }, + "node_modules/@kie-tools-core/notifications": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@kie-tools-core/notifications/-/notifications-10.0.0.tgz", + "integrity": "sha512-3JxorNGXeGftkDa1AwBV1+4PndDC0gIP1N1kVjKHRu59B+jEMdTdw4wKR29q5osz2pcbZjgrZ2GFuAmJBoLknA==", + "dependencies": { + "@kie-tools-core/i18n": "10.0.0", + "@kie-tools-core/workspace": "10.0.0" + } + }, + "node_modules/@kie-tools-core/operating-system": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@kie-tools-core/operating-system/-/operating-system-10.0.0.tgz", + "integrity": "sha512-TD7lWqgjMbAs8mPWhpBv6rXvvjHbGKoYDi8pOnLlAgj5ph4EUSFu1jU/zkIKcXKQbYVmEc7PRs5OwD7q+n/fTA==" + }, + "node_modules/@kie-tools-core/patternfly-base": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@kie-tools-core/patternfly-base/-/patternfly-base-10.0.0.tgz", + "integrity": "sha512-SA6ozINdiFLu0q93EEczhZesa5KPHevDlgleECmfk/YJgOX9GBb4PXjntZzu8wPjXWFwigWm2mC9ZvGRyYq/8Q==" + }, + "node_modules/@kie-tools-core/workspace": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@kie-tools-core/workspace/-/workspace-10.0.0.tgz", + "integrity": "sha512-cv6r+G37BI+56aGmasKO4yZB7Q6dtT42tXWDmsnAQLwuxxnSMnE5+MIHayD7sYntNop00EFT5pgBEyZV81ujnA==", + "dependencies": { + "@kie-tools-core/operating-system": "10.0.0" + } + }, + "node_modules/@kogito-tooling/kie-editors-standalone": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@kogito-tooling/kie-editors-standalone/-/kie-editors-standalone-0.16.0.tgz", + "integrity": "sha512-sludTj9AHWtTVT1g0F7YuhMtDvwiN1slBK56wKkO8KcTNJKi0k3RU8JktgnKFaLzmtVdcCQ0KUqabor/wUKPhQ==" + }, + "node_modules/@lezer/common": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", + "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==" + }, + "node_modules/@lezer/highlight": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", + "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/json": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz", + "integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", + "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/markdown": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.4.3.tgz", + "integrity": "sha512-kfw+2uMrQ/wy/+ONfrH83OkdFNM0ye5Xq96cLlaCy7h5UT9FO54DU4oRoIc0CSBh5NWmWuiIJA7NGLMJbQ+Oxg==", + "dependencies": { + "@lezer/common": "^1.0.0", + "@lezer/highlight": "^1.0.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==" + }, + "node_modules/@patternfly/react-core": { + "version": "4.278.1", + "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-4.278.1.tgz", + "integrity": "sha512-BZ+A0r/xLWXLxE5/b8FTVxRI/KokDlTQOS0ub49ts7nv++vmZS7kU4tn2bfuh7RVw/BfW4CNtoMzeJkM8GpaWw==", + "dependencies": { + "@patternfly/react-icons": "^4.93.7", + "@patternfly/react-styles": "^4.92.8", + "@patternfly/react-tokens": "^4.94.7", + "focus-trap": "6.9.2", + "react-dropzone": "9.0.0", + "tippy.js": "5.1.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17 || ^18", + "react-dom": "^16.8 || ^17 || ^18" + } + }, + "node_modules/@patternfly/react-core/node_modules/focus-trap": { + "version": "6.9.2", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-6.9.2.tgz", + "integrity": "sha512-gBEuXOPNOKPrLdZpMFUSTyIo1eT2NSZRrwZ9r/0Jqw5tmT3Yvxfmu8KBHw8xW2XQkw6E/JoG+OlEq7UDtSUNgw==", + "dependencies": { + "tabbable": "^5.3.2" + } + }, + "node_modules/@patternfly/react-core/node_modules/tabbable": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz", + "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==" + }, + "node_modules/@patternfly/react-icons": { + "version": "4.93.7", + "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.93.7.tgz", + "integrity": "sha512-3kr35dgba7Qz5CSzmfH0rIjSvBC5xkmiknf3SvVUVxaiVA7KRowID8viYHeZlf3v/Oa3sEewaH830Q0t+nWsZQ==", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18", + "react-dom": "^16.8 || ^17 || ^18" + } + }, + "node_modules/@patternfly/react-styles": { + "version": "4.92.8", + "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-4.92.8.tgz", + "integrity": "sha512-K4lUU8O4HiCX9NeuNUIrPgN3wlGERCxJVio+PAjd8hpJD/PKnjFfOJ9u6/Cii3qLy/5ZviWPRNHbGiwA/+YUhg==" + }, + "node_modules/@patternfly/react-tokens": { + "version": "4.94.7", + "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-4.94.7.tgz", + "integrity": "sha512-h+ducOLDMSxcuec3+YY3x+stM5ZUSnrl/lC/eVmjypil2El08NuE2MNEPMQWdhrod6VRRZFMNqZw/m82iv6U1A==" + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.1.tgz", + "integrity": "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.1.tgz", + "integrity": "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.1.tgz", + "integrity": "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.1.tgz", + "integrity": "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.1.tgz", + "integrity": "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.1.tgz", + "integrity": "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.1.tgz", + "integrity": "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.1.tgz", + "integrity": "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.1.tgz", + "integrity": "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.1.tgz", + "integrity": "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.1.tgz", + "integrity": "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.1.tgz", + "integrity": "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.1.tgz", + "integrity": "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.1.tgz", + "integrity": "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.1.tgz", + "integrity": "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.1.tgz", + "integrity": "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.1.tgz", + "integrity": "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.1.tgz", + "integrity": "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.1.tgz", + "integrity": "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.1.tgz", + "integrity": "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", + "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.3.tgz", + "integrity": "sha512-nhOb2dWPeb1sd3IQXL/dVPnKHDOAFfvichtBf4xV00/rU1QbPCQqKMbvIheIjqwVjh7qIgf2AHTHi391yMOMpQ==", + "dependencies": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", + "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==" + }, + "node_modules/@solidjs/router": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/@solidjs/router/-/router-0.15.3.tgz", + "integrity": "sha512-iEbW8UKok2Oio7o6Y4VTzLj+KFCmQPGEpm1fS3xixwFBdclFVBvaQVeibl1jys4cujfAK5Kn6+uG2uBm3lxOMw==", + "peerDependencies": { + "solid-js": "^1.8.6" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.8.tgz", + "integrity": "sha512-OWwBsbC9BFAJelmnNcrKuf+bka2ZxCE2A4Ft53Tkg4uoiE67r/PMEYwCsourC26E+kmxfwE0hVzMdxqeW+xu7Q==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.30.1", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.8" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.8.tgz", + "integrity": "sha512-d7qvv9PsM5N3VNKhwVUhpK6r4h9wtLkJ6lz9ZY9aeZgrUWk1Z8VPyqyDT9MZlem7GTGseRQHkeB1j3tC7W1P+A==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.8", + "@tailwindcss/oxide-darwin-arm64": "4.1.8", + "@tailwindcss/oxide-darwin-x64": "4.1.8", + "@tailwindcss/oxide-freebsd-x64": "4.1.8", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.8", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.8", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.8", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.8", + "@tailwindcss/oxide-linux-x64-musl": "4.1.8", + "@tailwindcss/oxide-wasm32-wasi": "4.1.8", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.8", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.8" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.8.tgz", + "integrity": "sha512-Fbz7qni62uKYceWYvUjRqhGfZKwhZDQhlrJKGtnZfuNtHFqa8wmr+Wn74CTWERiW2hn3mN5gTpOoxWKk0jRxjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.8.tgz", + "integrity": "sha512-RdRvedGsT0vwVVDztvyXhKpsU2ark/BjgG0huo4+2BluxdXo8NDgzl77qh0T1nUxmM11eXwR8jA39ibvSTbi7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.8.tgz", + "integrity": "sha512-t6PgxjEMLp5Ovf7uMb2OFmb3kqzVTPPakWpBIFzppk4JE4ix0yEtbtSjPbU8+PZETpaYMtXvss2Sdkx8Vs4XRw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.8.tgz", + "integrity": "sha512-g8C8eGEyhHTqwPStSwZNSrOlyx0bhK/V/+zX0Y+n7DoRUzyS8eMbVshVOLJTDDC+Qn9IJnilYbIKzpB9n4aBsg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.8.tgz", + "integrity": "sha512-Jmzr3FA4S2tHhaC6yCjac3rGf7hG9R6Gf2z9i9JFcuyy0u79HfQsh/thifbYTF2ic82KJovKKkIB6Z9TdNhCXQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.8.tgz", + "integrity": "sha512-qq7jXtO1+UEtCmCeBBIRDrPFIVI4ilEQ97qgBGdwXAARrUqSn/L9fUrkb1XP/mvVtoVeR2bt/0L77xx53bPZ/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.8.tgz", + "integrity": "sha512-O6b8QesPbJCRshsNApsOIpzKt3ztG35gfX9tEf4arD7mwNinsoCKxkj8TgEE0YRjmjtO3r9FlJnT/ENd9EVefQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.8.tgz", + "integrity": "sha512-32iEXX/pXwikshNOGnERAFwFSfiltmijMIAbUhnNyjFr3tmWmMJWQKU2vNcFX0DACSXJ3ZWcSkzNbaKTdngH6g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.8.tgz", + "integrity": "sha512-s+VSSD+TfZeMEsCaFaHTaY5YNj3Dri8rST09gMvYQKwPphacRG7wbuQ5ZJMIJXN/puxPcg/nU+ucvWguPpvBDg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.8.tgz", + "integrity": "sha512-CXBPVFkpDjM67sS1psWohZ6g/2/cd+cq56vPxK4JeawelxwK4YECgl9Y9TjkE2qfF+9/s1tHHJqrC4SS6cVvSg==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@emnapi/wasi-threads": "^1.0.2", + "@napi-rs/wasm-runtime": "^0.2.10", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.8.tgz", + "integrity": "sha512-7GmYk1n28teDHUjPlIx4Z6Z4hHEgvP5ZW2QS9ygnDAdI/myh3HTHjDqtSqgu1BpRoI4OiLx+fThAyA1JePoENA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.8.tgz", + "integrity": "sha512-fou+U20j+Jl0EHwK92spoWISON2OBnCazIc038Xj2TdweYV33ZRkS9nwqiUi2d/Wba5xg5UoHfvynnb/UB49cQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.8.tgz", + "integrity": "sha512-vB/vlf7rIky+w94aWMw34bWW1ka6g6C3xIOdICKX2GC0VcLtL6fhlLiafF0DVIwa9V6EHz8kbWMkS2s2QvvNlw==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.1.8", + "@tailwindcss/oxide": "4.1.8", + "postcss": "^8.4.41", + "tailwindcss": "4.1.8" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "24.0.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.1.tgz", + "integrity": "sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw==", + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "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==", + "optional": true + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/array-move": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/array-move/-/array-move-4.0.0.tgz", + "integrity": "sha512-+RY54S8OuVvg94THpneQvFRmqWdAHeqtMzgMW6JNurHxe8rsS07cHQdfGkXnTUXiBcyZ0j3SiDIxxj0RPiqCkQ==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/atoa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atoa/-/atoa-1.0.0.tgz", + "integrity": "sha512-VVE1H6cc4ai+ZXo/CRWoJiHXrA1qfA31DPnx6D20+kSI547hQN5Greh51LQ1baMRMfxO5K5M4ImMtZbZt2DODQ==" + }, + "node_modules/attr-accept": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.3.tgz", + "integrity": "sha512-iT40nudw8zmCweivz6j58g+RT33I4KbaIvRUhjNmDwO2WmsQUxFEZZYZ5w3vXe5x5MX9D7mfvA/XaLOZYFR9EQ==", + "dependencies": { + "core-js": "^2.5.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", + "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/babel-plugin-jsx-dom-expressions": { + "version": "0.39.8", + "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.39.8.tgz", + "integrity": "sha512-/MVOIIjonylDXnrWmG23ZX82m9mtKATsVHB7zYlPfDR9Vdd/NBE48if+wv27bSkBtyO7EPMUlcUc4J63QwuACQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "7.18.6", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/types": "^7.20.7", + "html-entities": "2.3.3", + "parse5": "^7.1.2", + "validate-html-nesting": "^1.2.1" + }, + "peerDependencies": { + "@babel/core": "^7.20.12" + } + }, + "node_modules/babel-plugin-jsx-dom-expressions/node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/babel-preset-solid": { + "version": "1.9.6", + "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.9.6.tgz", + "integrity": "sha512-HXTK9f93QxoH8dYn1M2mJdOlWgMsR88Lg/ul6QCZGkNTktjTE5HAf93YxQumHoCudLEtZrU1cFCMFOVho6GqFg==", + "dev": true, + "dependencies": { + "babel-plugin-jsx-dom-expressions": "^0.39.8" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/big.js": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-6.2.2.tgz", + "integrity": "sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==", + "engines": { + "node": "*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bigjs" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", + "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001718", + "electron-to-chromium": "^1.5.160", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-builder": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/buffer-builder/-/buffer-builder-0.2.0.tgz", + "integrity": "sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==", + "dev": true + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001720", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001720.tgz", + "integrity": "sha512-Ec/2yV2nNPwb4DnTANEV99ZWwm3ZWfdlfkQbWSDDt+PsXEVYwlhPH8tdMaPunYTKKmz7AnHi2oNEi1GcmKCD8g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/codemirror": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", + "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/colorjs.io": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz", + "integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/component-event": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/component-event/-/component-event-0.2.1.tgz", + "integrity": "sha512-wGA++isMqiDq1jPYeyv2as/Bt/u+3iLW0rEa+8NQ82jAv3TgqMiCM+B2SaBdn2DfLilLjjq736YcezihRYhfxw==" + }, + "node_modules/component-props": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/component-props/-/component-props-1.1.1.tgz", + "integrity": "sha512-69pIRJs9fCCHRqCz3390YF2LV1Lu6iEMZ5zuVqqUn+G20V9BNXlMs0cWawWeW9g4Ynmg29JmkG6R7/lUJoGd1Q==" + }, + "node_modules/component-xor": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/component-xor/-/component-xor-0.0.4.tgz", + "integrity": "sha512-ZIt6sla8gfo+AFVRZoZOertcnD5LJaY2T9CKE2j13NJxQt/mUafD69Bl7/Y4AnpI2LGjiXH7cOfJDx/n2G9edA==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/contra": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/contra/-/contra-1.9.4.tgz", + "integrity": "sha512-N9ArHAqwR/lhPq4OdIAwH4e1btn6EIZMAz4TazjnzCiVECcWUPTma+dRAM38ERImEJBh8NiCCpjoQruSZ+agYg==", + "dependencies": { + "atoa": "1.0.0", + "ticky": "1.0.1" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==" + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diagram-js": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/diagram-js/-/diagram-js-15.3.0.tgz", + "integrity": "sha512-Fjq+C8Bee2DhX9N3jpIUIhbqvSbP6oMD75n+y5UChcr34buApZFO+fXsv8a/SlW+tci+6nvc71vf17TBY/RJVg==", + "dependencies": { + "@bpmn-io/diagram-js-ui": "^0.2.3", + "clsx": "^2.1.0", + "didi": "^10.2.2", + "inherits-browser": "^0.1.0", + "min-dash": "^4.1.0", + "min-dom": "^4.2.1", + "object-refs": "^0.4.0", + "path-intersection": "^3.0.0", + "tiny-svg": "^3.1.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/diagram-js-direct-editing": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/diagram-js-direct-editing/-/diagram-js-direct-editing-3.2.0.tgz", + "integrity": "sha512-+pyxeQGBSdLiZX0/tmmsm2qZSvm9YtVzod5W3RMHSTR7VrkUMD6E7EX/W9JQv3ebxO7oIdqFmytmNDDpSHnYEw==", + "dependencies": { + "min-dash": "^4.0.0", + "min-dom": "^4.2.1" + }, + "engines": { + "node": "*" + }, + "peerDependencies": { + "diagram-js": "*" + } + }, + "node_modules/didi": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/didi/-/didi-10.2.2.tgz", + "integrity": "sha512-l8NYkYFXV1izHI65EyT8EXOjUZtKmQkHLTT89cSP7HU5J/G7AOj0dXKtLc04EXYlga99PBY18IPjOeZ+c3DI4w==", + "engines": { + "node": ">= 16" + } + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dmn-js": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/dmn-js/-/dmn-js-17.2.1.tgz", + "integrity": "sha512-xykSDlj3fM39p+75XAQnXypgmL/u5fOgyyEqaMBlqOrm0osgFw4J67GJpjK9aVmWyg7PgW3Re4Aom8YJ2C756g==", + "dependencies": { + "dmn-js-boxed-expression": "^17.2.1", + "dmn-js-decision-table": "^17.2.1", + "dmn-js-drd": "^17.2.1", + "dmn-js-literal-expression": "^17.2.1", + "dmn-js-shared": "^17.2.1" + } + }, + "node_modules/dmn-js-boxed-expression": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/dmn-js-boxed-expression/-/dmn-js-boxed-expression-17.2.1.tgz", + "integrity": "sha512-bivdADqjmaHW5qtVVxXlBqBMrlnqMbiEsdSNw8oTq1KB5+KtlzDkZItyNwZvyT2LPh5Z2869MUX3vdKvDPGjwA==", + "dependencies": { + "@bpmn-io/dmn-variable-resolver": "^0.7.0", + "diagram-js": "^15.2.0", + "dmn-js-shared": "^17.2.1", + "inferno": "~5.6.3", + "min-dash": "^4.2.2", + "min-dom": "^4.2.1", + "table-js": "^9.2.0" + } + }, + "node_modules/dmn-js-decision-table": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/dmn-js-decision-table/-/dmn-js-decision-table-17.2.1.tgz", + "integrity": "sha512-ZUqXDoaznTkJFkWgDInNyvSe5e/98HeNnLAZboQSHRUiiz1oWnRdv1WCgUOXc8jh0ZNNNDNFhGsTqJr2YhVnsg==", + "dependencies": { + "@bpmn-io/dmn-variable-resolver": "^0.7.0", + "css.escape": "^1.5.1", + "diagram-js": "^15.2.0", + "dmn-js-shared": "^17.2.1", + "escape-html": "^1.0.3", + "inferno": "~5.6.3", + "min-dash": "^4.2.2", + "min-dom": "^4.2.1", + "selection-ranges": "^3.0.2", + "table-js": "^9.2.0" + } + }, + "node_modules/dmn-js-drd": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/dmn-js-drd/-/dmn-js-drd-17.2.1.tgz", + "integrity": "sha512-8lkin3l5yPJ4+ZkqSede/5Zsv8avlNCOjGx68tXoe0GWRU06MeUswj8EB+70YKZ9xchjpTGpcTBj2EZIhx6K8A==", + "dependencies": { + "diagram-js": "^15.2.0", + "diagram-js-direct-editing": "^3.2.0", + "dmn-js-shared": "^17.2.1", + "inherits-browser": "^0.1.0", + "min-dash": "^4.2.2", + "min-dom": "^4.2.1", + "object-refs": "^0.4.0", + "tiny-svg": "^3.1.3" + } + }, + "node_modules/dmn-js-literal-expression": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/dmn-js-literal-expression/-/dmn-js-literal-expression-17.2.1.tgz", + "integrity": "sha512-a25K4Vj23E9EJzCnNrrMbCHyjjnLXNHb+vX9liddp8TSJDlUWwA+ZYMTs2oKBlVyEpK+Xj//dOxJ0kSpFhwYSg==", + "dependencies": { + "@bpmn-io/dmn-variable-resolver": "^0.7.0", + "diagram-js": "^15.2.0", + "dmn-js-shared": "^17.2.1", + "escape-html": "^1.0.3", + "inferno": "~5.6.3", + "min-dash": "^4.2.2", + "min-dom": "^4.2.1", + "table-js": "^9.2.0" + } + }, + "node_modules/dmn-js-shared": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/dmn-js-shared/-/dmn-js-shared-17.2.1.tgz", + "integrity": "sha512-tZ7TRf783dXeHULS113JopqDePfa5WZ0IfAzfn8bYSikBkARZlNrOvuhlfIxRIVdiFPa6fDDtum8XuVh6aviIQ==", + "dependencies": { + "@bpmn-io/feel-editor": "^1.10.1", + "diagram-js": "^15.2.0", + "didi": "^10.2.2", + "dmn-moddle": "^10.0.0", + "ids": "^1.0.5", + "inferno": "~5.6.3", + "min-dash": "^4.2.2", + "min-dom": "^4.2.1", + "selection-ranges": "^3.0.2", + "selection-update": "^0.1.2", + "table-js": "^9.2.0" + } + }, + "node_modules/dmn-moddle": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dmn-moddle/-/dmn-moddle-10.0.0.tgz", + "integrity": "sha512-JD08qH2VqA7O54qCQFrGruwGyVovow+9g2O5/ww0KpC/n0mvuua117dz2CONiUWexJxq/dOAaMjrQwkWnK+oEA==", + "dependencies": { + "min-dash": "^3.0.0", + "moddle": "^5.0.1", + "moddle-xml": "^9.0.5" + } + }, + "node_modules/dmn-moddle/node_modules/min-dash": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/min-dash/-/min-dash-3.8.1.tgz", + "integrity": "sha512-evumdlmIlg9mbRVPbC4F5FuRhNmcMS5pvuBUbqb1G9v09Ro0ImPEgz5n3khir83lFok1inKqVDjnKEg3GpDxQg==" + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dom-iterator": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/dom-iterator/-/dom-iterator-1.0.2.tgz", + "integrity": "sha512-BMelEjhy08OpoWF3v/jrPtx7PZCyP1VM9yiB7rJk19UVmt7zTN5rqoC0Jea+EyT0M6v/VokL0LxIlGLUOBJZ2g==", + "dependencies": { + "component-props": "1.1.1", + "component-xor": "0.0.4" + } + }, + "node_modules/domify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/domify/-/domify-1.4.2.tgz", + "integrity": "sha512-m4yreHcUWHBncGVV7U+yQzc12vIlq0jMrtHZ5mW6dQMiL/7skSYNVX9wqKwOtyO9SGCgevrAFEgOCAHmamHTUA==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dompurify": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz", + "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/downloadjs": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/downloadjs/-/downloadjs-1.4.7.tgz", + "integrity": "sha512-LN1gO7+u9xjU5oEScGFKvXhYf7Y/empUIIEAGBs1LzUq/rg5duiDrkuH5A2lQGd5jfMOb9X9usDa2oVXwJ0U/Q==" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.161", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.161.tgz", + "integrity": "sha512-hwtetwfKNZo/UlwHIVBlKZVdy7o8bIZxxKs0Mv/ROPiQQQmDgdm5a+KvKtBsxM8ZjFzTaCeLoodZ8jiBE3o9rA==", + "dev": true + }, + "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==" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", + "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", + "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.5", + "@esbuild/android-arm": "0.25.5", + "@esbuild/android-arm64": "0.25.5", + "@esbuild/android-x64": "0.25.5", + "@esbuild/darwin-arm64": "0.25.5", + "@esbuild/darwin-x64": "0.25.5", + "@esbuild/freebsd-arm64": "0.25.5", + "@esbuild/freebsd-x64": "0.25.5", + "@esbuild/linux-arm": "0.25.5", + "@esbuild/linux-arm64": "0.25.5", + "@esbuild/linux-ia32": "0.25.5", + "@esbuild/linux-loong64": "0.25.5", + "@esbuild/linux-mips64el": "0.25.5", + "@esbuild/linux-ppc64": "0.25.5", + "@esbuild/linux-riscv64": "0.25.5", + "@esbuild/linux-s390x": "0.25.5", + "@esbuild/linux-x64": "0.25.5", + "@esbuild/netbsd-arm64": "0.25.5", + "@esbuild/netbsd-x64": "0.25.5", + "@esbuild/openbsd-arm64": "0.25.5", + "@esbuild/openbsd-x64": "0.25.5", + "@esbuild/sunos-x64": "0.25.5", + "@esbuild/win32-arm64": "0.25.5", + "@esbuild/win32-ia32": "0.25.5", + "@esbuild/win32-x64": "0.25.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/fast-xml-parser": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", + "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "dependencies": { + "strnum": "^1.1.1" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fdir": { + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz", + "integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==", + "dev": true, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/feelers": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/feelers/-/feelers-1.4.0.tgz", + "integrity": "sha512-CGa/7ILuqoqTaeYeoKsg/4tzu2es9sEEJTmSjdu0lousZBw4V9gcYhHYFNmbrSrKmbAVfOzj6/DsymGJWFIOeg==", + "dependencies": { + "@bpmn-io/cm-theme": "^0.1.0-alpha.2", + "@bpmn-io/feel-lint": "^1.2.0", + "@codemirror/autocomplete": "^6.10.1", + "@codemirror/commands": "^6.3.0", + "@codemirror/language": "^6.9.1", + "@codemirror/lint": "^6.4.2", + "@codemirror/state": "^6.3.0", + "@codemirror/view": "^6.21.3", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.1.6", + "@lezer/lr": "^1.3.13", + "@lezer/markdown": "^1.1.0", + "feelin": "^3.0.1", + "lezer-feel": "^1.2.4", + "min-dom": "^5.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/feelers/node_modules/domify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/domify/-/domify-2.0.0.tgz", + "integrity": "sha512-rmvrrmWQPD/X1A/nPBfIVg4r05792QdG9Z4Prk6oQG0F9zBMDkr0GKAdds1wjb2dq1rTz/ywc4ZxpZbgz0tttg==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/feelers/node_modules/feelin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/feelin/-/feelin-3.2.0.tgz", + "integrity": "sha512-GFDbHsTYk7YXO1tyw1dOjb7IODeAZvNIosdGZThUwPx5XcD/XhO0hnPZXsIbAzSsIdrgGlTEEdby9fZ2gixysA==", + "dependencies": { + "@lezer/lr": "^1.4.2", + "lezer-feel": "^1.4.0", + "luxon": "^3.5.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/feelers/node_modules/min-dom": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/min-dom/-/min-dom-5.1.1.tgz", + "integrity": "sha512-GaKUlguMAofd3OJsB0OkP17i5kucKqErgVCJxPawO9l5NwIPnr28SAr99zzlzMCWWljISBYrnZVWdE2Q92YGFQ==", + "dependencies": { + "domify": "^2.0.0", + "min-dash": "^4.2.1" + } + }, + "node_modules/feelin": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/feelin/-/feelin-4.3.0.tgz", + "integrity": "sha512-Uv3YNCrmXSXgkCJSfa2jR7SL/acjPhfEHKNsaL///fRJxD7epUvUJyF1WXbh8oh9Myv+z3WVClGb+rIuT7LDow==", + "dependencies": { + "@lezer/lr": "^1.4.2", + "lezer-feel": "^1.6.0", + "luxon": "^3.5.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/file-drops": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/file-drops/-/file-drops-0.5.0.tgz", + "integrity": "sha512-ZaENKwVySae4RhEGjh1gEE1wMnIIPG6XqtOwHNQYSl7RNwUHoRGVVspe+BrW7cUFseHNIit3Oy9Z/HPIEU5XWA==", + "dependencies": { + "min-dom": "^4.0.3" + } + }, + "node_modules/file-selector": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.1.19.tgz", + "integrity": "sha512-kCWw3+Aai8Uox+5tHCNgMFaUdgidxvMnLWO6fM5sZ0hA2wlHP5/DHGF0ECe84BiB95qdJbKNEJhWKVDvMN+JDQ==", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/firebase": { + "version": "11.9.1", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-11.9.1.tgz", + "integrity": "sha512-nbQbQxNlkHHRDn4cYwHdAKHwJPeZ0jRXxlNp6PCOb9CQx8Dc6Vjve97R34r1EZJnzOsPYZ3+ssJH7fkovDjvCw==", + "dependencies": { + "@firebase/ai": "1.4.0", + "@firebase/analytics": "0.10.16", + "@firebase/analytics-compat": "0.2.22", + "@firebase/app": "0.13.1", + "@firebase/app-check": "0.10.0", + "@firebase/app-check-compat": "0.3.25", + "@firebase/app-compat": "0.4.1", + "@firebase/app-types": "0.9.3", + "@firebase/auth": "1.10.7", + "@firebase/auth-compat": "0.5.27", + "@firebase/data-connect": "0.3.9", + "@firebase/database": "1.0.19", + "@firebase/database-compat": "2.0.10", + "@firebase/firestore": "4.7.17", + "@firebase/firestore-compat": "0.3.52", + "@firebase/functions": "0.12.8", + "@firebase/functions-compat": "0.3.25", + "@firebase/installations": "0.6.17", + "@firebase/installations-compat": "0.2.17", + "@firebase/messaging": "0.12.21", + "@firebase/messaging-compat": "0.2.21", + "@firebase/performance": "0.7.6", + "@firebase/performance-compat": "0.2.19", + "@firebase/remote-config": "0.6.4", + "@firebase/remote-config-compat": "0.2.17", + "@firebase/storage": "0.13.13", + "@firebase/storage-compat": "0.3.23", + "@firebase/util": "1.12.0" + } + }, + "node_modules/flatpickr": { + "version": "4.6.13", + "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz", + "integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw==" + }, + "node_modules/focus-trap": { + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.5.tgz", + "integrity": "sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg==", + "dependencies": { + "tabbable": "^6.2.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/htm": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/htm/-/htm-3.1.1.tgz", + "integrity": "sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ==" + }, + "node_modules/html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", + "dev": true + }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==" + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, + "node_modules/ids": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/ids/-/ids-1.0.5.tgz", + "integrity": "sha512-XQ0yom/4KWTL29sLG+tyuycy7UmeaM/79GRtSJq6IG9cJGIPeBz5kwDCguie3TwxaMNIc3WtPi0cTa1XYHicpw==" + }, + "node_modules/immutable": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", + "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", + "dev": true + }, + "node_modules/inferno": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/inferno/-/inferno-5.6.3.tgz", + "integrity": "sha512-2m7bEo/6D+pbxFkFKY/2QOVdknxxXyrXyDdVFobJcQyvfv3985MY+VVwyRZoVJylQG+mzPY5/uDNuXygp8jObA==", + "hasInstallScript": true, + "dependencies": { + "inferno-shared": "5.6.3", + "inferno-vnode-flags": "5.6.3", + "opencollective-postinstall": "^2.0.3" + } + }, + "node_modules/inferno-shared": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/inferno-shared/-/inferno-shared-5.6.3.tgz", + "integrity": "sha512-NzhS62Y6BwfkLNmXS9C0Ie7skY1vIoLrVIYnVrMx1xfTLp/AG+ths4oAJGmWcX/AkVSYl+DjP96HEuUdtGmRRw==" + }, + "node_modules/inferno-vnode-flags": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/inferno-vnode-flags/-/inferno-vnode-flags-5.6.3.tgz", + "integrity": "sha512-1V9Pjz5Erm2sP7xlb4D2zW/U8e3RwU41Ruyn9/apke0BFxbLI+LxxVcKWxom4fPetG6YcBNLpZssy8gkigT9gg==" + }, + "node_modules/inherits-browser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/inherits-browser/-/inherits-browser-0.1.0.tgz", + "integrity": "sha512-CJHHvW3jQ6q7lzsXPpapLdMx5hDpSF3FSh45pwsj6bKxJJ8Nl8v43i5yXnr3BdfOimGHKyniewQtnAIp3vyJJw==" + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "dev": true, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "dev": true, + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/just-extend": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==" + }, + "node_modules/lang-feel": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/lang-feel/-/lang-feel-2.3.0.tgz", + "integrity": "sha512-cotBfyBP710udy3Tm7s4NyNZPSSLXkVV/rrfmM4NVbuzB9WGL7CbMWUzfSn6GZ+qFnh8/xbkeDHfAvPM90oENA==", + "dependencies": { + "@codemirror/autocomplete": "^6.18.4", + "@codemirror/language": "^6.10.8", + "@lezer/common": "^1.2.3", + "lezer-feel": "^1.7.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/lezer-feel": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/lezer-feel/-/lezer-feel-1.7.0.tgz", + "integrity": "sha512-UC8h3Nu4llRPISEUhv+Ne7bNkdxjf4+/DcU4KfO8zKxycWxev8d2BoVnGlG17zbQDtQJBD39ZQvWtjCeTFm69g==", + "dependencies": { + "@lezer/highlight": "^1.2.1", + "@lezer/lr": "^1.4.2", + "min-dash": "^4.2.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "dev": true, + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead." + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/luxon": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.6.1.tgz", + "integrity": "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/marked": { + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge-anything": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/merge-anything/-/merge-anything-5.1.7.tgz", + "integrity": "sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==", + "dev": true, + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/min-dash": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/min-dash/-/min-dash-4.2.3.tgz", + "integrity": "sha512-VLMYQI5+FcD9Ad24VcB08uA83B07OhueAlZ88jBK6PyupTvEJwllTMUqMy0wPGYs7pZUEtEEMWdHB63m3LtEcg==" + }, + "node_modules/min-dom": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/min-dom/-/min-dom-4.2.1.tgz", + "integrity": "sha512-TMoL8SEEIhUWYgkj7XMSgxmwSyGI+4fP2KFFGnN3FbHfbGHVdsLYSz8LoIsgPhz4dWRmLvxWWSMgzZMJW5sZuA==", + "dependencies": { + "component-event": "^0.2.1", + "domify": "^1.4.1", + "min-dash": "^4.2.1" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dev": true, + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/moddle": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/moddle/-/moddle-5.0.4.tgz", + "integrity": "sha512-Kjb+hjuzO+YlojNGxEUXvdhLYTHTtAABDlDcJTtTcn5MbJF9Zkv4I1Fyvp3Ypmfgg1EfHDZ3PsCQTuML9JD6wg==", + "dependencies": { + "min-dash": "^3.0.0" + } + }, + "node_modules/moddle-xml": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/moddle-xml/-/moddle-xml-9.0.6.tgz", + "integrity": "sha512-tl0reHpsY/aKlLGhXeFlQWlYAQHFxTkFqC8tq8jXRYpQSnLVw13T6swMaourLd7EXqHdWsc+5ggsB+fEep6xZQ==", + "dependencies": { + "min-dash": "^3.5.2", + "moddle": "^5.0.2", + "saxen": "^8.1.2" + } + }, + "node_modules/moddle-xml/node_modules/min-dash": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/min-dash/-/min-dash-3.8.1.tgz", + "integrity": "sha512-evumdlmIlg9mbRVPbC4F5FuRhNmcMS5pvuBUbqb1G9v09Ro0ImPEgz5n3khir83lFok1inKqVDjnKEg3GpDxQg==" + }, + "node_modules/moddle/node_modules/min-dash": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/min-dash/-/min-dash-3.8.1.tgz", + "integrity": "sha512-evumdlmIlg9mbRVPbC4F5FuRhNmcMS5pvuBUbqb1G9v09Ro0ImPEgz5n3khir83lFok1inKqVDjnKEg3GpDxQg==" + }, + "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==" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nise": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", + "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==", + "dependencies": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/text-encoding": "^0.7.2", + "just-extend": "^6.2.0", + "path-to-regexp": "^6.2.1" + } + }, + "node_modules/nise/node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/nise/node_modules/@sinonjs/fake-timers": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.3.1.tgz", + "integrity": "sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-refs": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-refs/-/object-refs-0.4.0.tgz", + "integrity": "sha512-6kJqKWryKZmtte6QYvouas0/EIJKPI1/MMIuRsiBlNuhIMfqYTggzX2F1AJ2+cDs288xyi9GL7FyasHINR98BQ==", + "engines": { + "node": "*" + } + }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "bin": { + "opencollective-postinstall": "index.js" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-intersection": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/path-intersection/-/path-intersection-3.1.0.tgz", + "integrity": "sha512-3xS3lvv/vuwm5aH2BVvNRvnvwR2Drde7jQClKpCXTYXIMMjcw/EnMhzCgeHwqbCpzi760PEfAkU53vSIlrNr9A==", + "engines": { + "node": ">= 14.20" + } + }, + "node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", + "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/portfinder": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.37.tgz", + "integrity": "sha512-yuGIEjDAYnnOex9ddMnKZEMFE0CcGo6zbfzDklkmT1m5z734ss6JMzN9rNB3+RR7iS+F10D4/BVIaXOyh8PQKw==", + "dependencies": { + "async": "^3.2.6", + "debug": "^4.3.6" + }, + "engines": { + "node": ">= 10.12" + } + }, + "node_modules/postcss": { + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz", + "integrity": "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==", + "dev": true, + "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" + } + ], + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/preact": { + "version": "10.26.8", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.26.8.tgz", + "integrity": "sha512-1nMfdFjucm5hKvq0IClqZwK4FJkGXhRrQstOQ3P4vp8HxKrJEMFcY6RdBRVTdfQS/UlnX6gfbPuTvaqx/bDoeQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, + "node_modules/protobufjs": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.3.tgz", + "integrity": "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + }, + "peerDependencies": { + "react": "17.0.2" + } + }, + "node_modules/react-dropzone": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-9.0.0.tgz", + "integrity": "sha512-wZ2o9B2qkdE3RumWhfyZT9swgJYJPeU5qHEcMU8weYpmLex1eeWX0CC32/Y0VutB+BBi2D+iePV/YZIiB4kZGw==", + "dependencies": { + "attr-accept": "^1.1.3", + "file-selector": "^0.1.8", + "prop-types": "^15.6.2", + "prop-types-extra": "^1.1.0" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.1.tgz", + "integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.41.1", + "@rollup/rollup-android-arm64": "4.41.1", + "@rollup/rollup-darwin-arm64": "4.41.1", + "@rollup/rollup-darwin-x64": "4.41.1", + "@rollup/rollup-freebsd-arm64": "4.41.1", + "@rollup/rollup-freebsd-x64": "4.41.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.41.1", + "@rollup/rollup-linux-arm-musleabihf": "4.41.1", + "@rollup/rollup-linux-arm64-gnu": "4.41.1", + "@rollup/rollup-linux-arm64-musl": "4.41.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.41.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.41.1", + "@rollup/rollup-linux-riscv64-gnu": "4.41.1", + "@rollup/rollup-linux-riscv64-musl": "4.41.1", + "@rollup/rollup-linux-s390x-gnu": "4.41.1", + "@rollup/rollup-linux-x64-gnu": "4.41.1", + "@rollup/rollup-linux-x64-musl": "4.41.1", + "@rollup/rollup-win32-arm64-msvc": "4.41.1", + "@rollup/rollup-win32-ia32-msvc": "4.41.1", + "@rollup/rollup-win32-x64-msvc": "4.41.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/sass-embedded": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.89.2.tgz", + "integrity": "sha512-Ack2K8rc57kCFcYlf3HXpZEJFNUX8xd8DILldksREmYXQkRHI879yy8q4mRDJgrojkySMZqmmmW1NxrFxMsYaA==", + "dev": true, + "dependencies": { + "@bufbuild/protobuf": "^2.5.0", + "buffer-builder": "^0.2.0", + "colorjs.io": "^0.5.0", + "immutable": "^5.0.2", + "rxjs": "^7.4.0", + "supports-color": "^8.1.1", + "sync-child-process": "^1.0.2", + "varint": "^6.0.0" + }, + "bin": { + "sass": "dist/bin/sass.js" + }, + "engines": { + "node": ">=16.0.0" + }, + "optionalDependencies": { + "sass-embedded-android-arm": "1.89.2", + "sass-embedded-android-arm64": "1.89.2", + "sass-embedded-android-riscv64": "1.89.2", + "sass-embedded-android-x64": "1.89.2", + "sass-embedded-darwin-arm64": "1.89.2", + "sass-embedded-darwin-x64": "1.89.2", + "sass-embedded-linux-arm": "1.89.2", + "sass-embedded-linux-arm64": "1.89.2", + "sass-embedded-linux-musl-arm": "1.89.2", + "sass-embedded-linux-musl-arm64": "1.89.2", + "sass-embedded-linux-musl-riscv64": "1.89.2", + "sass-embedded-linux-musl-x64": "1.89.2", + "sass-embedded-linux-riscv64": "1.89.2", + "sass-embedded-linux-x64": "1.89.2", + "sass-embedded-win32-arm64": "1.89.2", + "sass-embedded-win32-x64": "1.89.2" + } + }, + "node_modules/sass-embedded-android-arm": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.89.2.tgz", + "integrity": "sha512-oHAPTboBHRZlDBhyRB6dvDKh4KvFs+DZibDHXbkSI6dBZxMTT+Yb2ivocHnctVGucKTLQeT7+OM5DjWHyynL/A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-android-arm64": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.89.2.tgz", + "integrity": "sha512-+pq7a7AUpItNyPu61sRlP6G2A8pSPpyazASb+8AK2pVlFayCSPAEgpwpCE9A2/Xj86xJZeMizzKUHxM2CBCUxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-android-riscv64": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.89.2.tgz", + "integrity": "sha512-HfJJWp/S6XSYvlGAqNdakeEMPOdhBkj2s2lN6SHnON54rahKem+z9pUbCriUJfM65Z90lakdGuOfidY61R9TYg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-android-x64": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.89.2.tgz", + "integrity": "sha512-BGPzq53VH5z5HN8de6jfMqJjnRe1E6sfnCWFd4pK+CAiuM7iw5Fx6BQZu3ikfI1l2GY0y6pRXzsVLdp/j4EKEA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-darwin-arm64": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.89.2.tgz", + "integrity": "sha512-UCm3RL/tzMpG7DsubARsvGUNXC5pgfQvP+RRFJo9XPIi6elopY5B6H4m9dRYDpHA+scjVthdiDwkPYr9+S/KGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-darwin-x64": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.89.2.tgz", + "integrity": "sha512-D9WxtDY5VYtMApXRuhQK9VkPHB8R79NIIR6xxVlN2MIdEid/TZWi1MHNweieETXhWGrKhRKglwnHxxyKdJYMnA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-arm": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.89.2.tgz", + "integrity": "sha512-leP0t5U4r95dc90o8TCWfxNXwMAsQhpWxTkdtySDpngoqtTy3miMd7EYNYd1znI0FN1CBaUvbdCMbnbPwygDlA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-arm64": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.89.2.tgz", + "integrity": "sha512-2N4WW5LLsbtrWUJ7iTpjvhajGIbmDR18ZzYRywHdMLpfdPApuHPMDF5CYzHbS+LLx2UAx7CFKBnj5LLjY6eFgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-arm": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.89.2.tgz", + "integrity": "sha512-Z6gG2FiVEEdxYHRi2sS5VIYBmp17351bWtOCUZ/thBM66+e70yiN6Eyqjz80DjL8haRUegNQgy9ZJqsLAAmr9g==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-arm64": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.89.2.tgz", + "integrity": "sha512-nTyuaBX6U1A/cG7WJh0pKD1gY8hbg1m2SnzsyoFG+exQ0lBX/lwTLHq3nyhF+0atv7YYhYKbmfz+sjPP8CZ9lw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-riscv64": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.89.2.tgz", + "integrity": "sha512-N6oul+qALO0SwGY8JW7H/Vs0oZIMrRMBM4GqX3AjM/6y8JsJRxkAwnfd0fDyK+aICMFarDqQonQNIx99gdTZqw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-x64": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.89.2.tgz", + "integrity": "sha512-K+FmWcdj/uyP8GiG9foxOCPfb5OAZG0uSVq80DKgVSC0U44AdGjvAvVZkrgFEcZ6cCqlNC2JfYmslB5iqdL7tg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-riscv64": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.89.2.tgz", + "integrity": "sha512-g9nTbnD/3yhOaskeqeBQETbtfDQWRgsjHok6bn7DdAuwBsyrR3JlSFyqKc46pn9Xxd9SQQZU8AzM4IR+sY0A0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-x64": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.89.2.tgz", + "integrity": "sha512-Ax7dKvzncyQzIl4r7012KCMBvJzOz4uwSNoyoM5IV6y5I1f5hEwI25+U4WfuTqdkv42taCMgpjZbh9ERr6JVMQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-win32-arm64": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.89.2.tgz", + "integrity": "sha512-j96iJni50ZUsfD6tRxDQE2QSYQ2WrfHxeiyAXf41Kw0V4w5KYR/Sf6rCZQLMTUOHnD16qTMVpQi20LQSqf4WGg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-win32-x64": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.89.2.tgz", + "integrity": "sha512-cS2j5ljdkQsb4PaORiClaVYynE9OAPZG/XjbOMxpQmjRIf7UroY4PEIH+Waf+y47PfXFX9SyxhYuw2NIKGbEng==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/saxen": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/saxen/-/saxen-8.1.2.tgz", + "integrity": "sha512-xUOiiFbc3Ow7p8KMxwsGICPx46ZQvy3+qfNVhrkwfz3Vvq45eGt98Ft5IQaA1R/7Tb5B5MKh9fUR9x3c3nDTxw==" + }, + "node_modules/scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "node_modules/selection-ranges": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/selection-ranges/-/selection-ranges-3.0.3.tgz", + "integrity": "sha512-60Oqc07j16YCrp96uITgBFu7oT81JKMmL+cOOcxe3jvuGSiFSwsLpOSXNBAlITV9hGhEl1H6P/+g1bKnpfXoSw==", + "dependencies": { + "dom-iterator": "^1.0.0" + } + }, + "node_modules/selection-update": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/selection-update/-/selection-update-0.1.2.tgz", + "integrity": "sha512-4jzoJNh7VT2s2tvm/kUSskSw7pD0BVcrrGccbfOMK+3AXLBPz6nIy1yo+pbXgvNoTNII96Pq92+sAY+rF0LUAA==" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/seroval": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.2.tgz", + "integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/seroval-plugins": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.3.2.tgz", + "integrity": "sha512-0QvCV2lM3aj/U3YozDiVwx9zpH0q8A60CTWIv4Jszj/givcudPb48B+rkU5D51NJ0pTpweGMttHjboPa9/zoIQ==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "seroval": "^1.0" + } + }, + "node_modules/sinon": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-11.1.2.tgz", + "integrity": "sha512-59237HChms4kg7/sXhiRcUzdSkKuydDeTiamT/jesUVHshBgL8XAmhgFo0GfK6RruMDM/iRSij1EybmMog9cJw==", + "deprecated": "16.1.1", + "dependencies": { + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": "^7.1.2", + "@sinonjs/samsam": "^6.0.2", + "diff": "^5.0.0", + "nise": "^5.1.0", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/solid-bootstrap": { + "version": "1.0.21", + "resolved": "https://registry.npmjs.org/solid-bootstrap/-/solid-bootstrap-1.0.21.tgz", + "integrity": "sha512-B1+1qsdqrdQhl4xgmgrFRafxOM3xrnafxrC1scbOTYYJI2ido1XfoxtYyJ72W1eSHxYjm8eRGXdGTGdBJsGPpg==", + "dependencies": { + "dom-helpers": "^5.2.0", + "solid-bootstrap-core": "^2.0.0", + "solid-react-transition": "^1.0.11" + }, + "peerDependencies": { + "solid-js": ">=1.6.0" + } + }, + "node_modules/solid-bootstrap-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/solid-bootstrap-core/-/solid-bootstrap-core-2.0.0.tgz", + "integrity": "sha512-tw1me1iEvI+UzYRL2Gs53MP51WVwx785CU+6KQVGhaLESw3OoayeFLhe1CvUYb7kuskjtNGyAorZMdMwBJQIBA==", + "dependencies": { + "@popperjs/core": "^2.10.1", + "dom-helpers": "^5.2.0", + "solid-react-transition": "^1.0.8" + }, + "peerDependencies": { + "solid-js": ">=1.6.0" + } + }, + "node_modules/solid-js": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.7.tgz", + "integrity": "sha512-/saTKi8iWEM233n5OSi1YHCCuh66ZIQ7aK2hsToPe4tqGm7qAejU1SwNuTPivbWAYq7SjuHVVYxxuZQNRbICiw==", + "dependencies": { + "csstype": "^3.1.0", + "seroval": "~1.3.0", + "seroval-plugins": "~1.3.0" + } + }, + "node_modules/solid-react-transition": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/solid-react-transition/-/solid-react-transition-1.0.11.tgz", + "integrity": "sha512-YMT7z6sOupCicDtX19156vbVOm3vCIgjVhPTybR9gLKiIPrDB2NDVqnQk4kpNCDZTOwSjLTOyUQw0xJnXgDg2A==", + "peerDependencies": { + "solid-js": ">=1.6.0" + } + }, + "node_modules/solid-refresh": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/solid-refresh/-/solid-refresh-0.6.3.tgz", + "integrity": "sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==", + "dev": true, + "dependencies": { + "@babel/generator": "^7.23.6", + "@babel/helper-module-imports": "^7.22.15", + "@babel/types": "^7.23.6" + }, + "peerDependencies": { + "solid-js": "^1.3" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ] + }, + "node_modules/style-mod": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sync-child-process": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz", + "integrity": "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==", + "dev": true, + "dependencies": { + "sync-message-port": "^1.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/sync-message-port": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sync-message-port/-/sync-message-port-1.1.3.tgz", + "integrity": "sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==", + "dev": true, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" + }, + "node_modules/table-js": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/table-js/-/table-js-9.2.0.tgz", + "integrity": "sha512-TcphjV4hN3NKDJkAqsTRn7N0dLaO7ST3LwEZnwYMjDkyBNG6kzTHBbN9b6nlvuxxiVxWHFuc/REU1FR2mh0oLA==", + "dependencies": { + "didi": "^10.0.0", + "ids": "^1.0.0", + "min-dash": "^4.0.0", + "min-dom": "^4.0.3", + "selection-ranges": "^4.0.0" + }, + "engines": { + "node": "*" + }, + "peerDependencies": { + "diagram-js": "^11.3.0 || ^12 || ^13 || ^14 || ^15", + "inferno": "^5.0.5" + } + }, + "node_modules/table-js/node_modules/selection-ranges": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/selection-ranges/-/selection-ranges-4.0.3.tgz", + "integrity": "sha512-GwzYG/idGEF+DxbyWCpPKS0QO8voQX8EE3IzpzsLG8teRt3E5xXW5XU36OJ1NoLfrduhdWqYNdncQqLy0HNhqQ==", + "dependencies": { + "dom-iterator": "^1.0.2" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.8.tgz", + "integrity": "sha512-kjeW8gjdxasbmFKpVGrGd5T4i40mV5J2Rasw48QARfYeQ8YS9x02ON9SFWax3Qf616rt4Cp3nVNIj6Hd1mP3og==", + "dev": true + }, + "node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/ticky": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ticky/-/ticky-1.0.1.tgz", + "integrity": "sha512-RX35iq/D+lrsqhcPWIazM9ELkjOe30MSeoBHQHSsRwd1YuhJO5ui1K1/R0r7N3mFvbLBs33idw+eR6j+w6i/DA==" + }, + "node_modules/tiny-svg": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/tiny-svg/-/tiny-svg-3.1.3.tgz", + "integrity": "sha512-9mwnPqXInRsBmH/DO6NMxBE++9LsqpVXQSSTZGc5bomoKKvL5OX/Hlotw7XVXP6XLRcHWIzZpxfovGqWKgCypQ==" + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tippy.js": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-5.1.2.tgz", + "integrity": "sha512-Qtrv2wqbRbaKMUb6bWWBQWPayvcDKNrGlvihxtsyowhT7RLGEh1STWuy6EMXC6QLkfKPB2MLnf8W2mzql9VDAw==", + "dependencies": { + "popper.js": "^1.16.0" + } + }, + "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==" + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/validate-html-nesting": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/validate-html-nesting/-/validate-html-nesting-1.2.2.tgz", + "integrity": "sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==", + "dev": true + }, + "node_modules/varint": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", + "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==", + "dev": true + }, + "node_modules/vite": { + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "dev": true, + "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-plugin-solid": { + "version": "2.11.6", + "resolved": "https://registry.npmjs.org/vite-plugin-solid/-/vite-plugin-solid-2.11.6.tgz", + "integrity": "sha512-Sl5CTqJTGyEeOsmdH6BOgalIZlwH3t4/y0RQuFLMGnvWMBvxb4+lq7x3BSiAw6etf0QexfNJW7HSOO/Qf7pigg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.3", + "@types/babel__core": "^7.20.4", + "babel-preset-solid": "^1.8.4", + "merge-anything": "^5.1.7", + "solid-refresh": "^0.6.3", + "vitefu": "^1.0.4" + }, + "peerDependencies": { + "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", + "solid-js": "^1.7.2", + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "@testing-library/jest-dom": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.6.tgz", + "integrity": "sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA==", + "dev": true, + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" + }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/web-vitals": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "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==", + "engines": { + "node": ">=12" + } + } + } +} diff --git a/builder-frontend/package.json b/builder-frontend/package.json new file mode 100644 index 0000000..ac4604f --- /dev/null +++ b/builder-frontend/package.json @@ -0,0 +1,32 @@ +{ + "name": "builder-frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@bpmn-io/form-js": "^1.15.2", + "@bpmn-io/form-js-editor": "^1.15.2", + "@kie-tools-core/editor": "^10.0.0", + "@kogito-tooling/kie-editors-standalone": "^0.16.0", + "@solidjs/router": "^0.15.3", + "dmn-js": "^17.2.1", + "firebase": "^11.9.1", + "lodash.debounce": "^4.0.8", + "solid-bootstrap": "^1.0.21", + "solid-js": "^1.9.5" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.1.8", + "autoprefixer": "^10.4.21", + "postcss": "^8.5.4", + "sass-embedded": "^1.89.2", + "tailwindcss": "^4.1.8", + "vite": "^6.3.5", + "vite-plugin-solid": "^2.11.6" + } +} diff --git a/builder-frontend/postcss.config.js b/builder-frontend/postcss.config.js new file mode 100644 index 0000000..f69c5d4 --- /dev/null +++ b/builder-frontend/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + "@tailwindcss/postcss": {}, + autoprefixer: {}, + }, +}; diff --git a/builder-frontend/public/dmn/list-types.dmn b/builder-frontend/public/dmn/list-types.dmn new file mode 100644 index 0000000..f953022 --- /dev/null +++ b/builder-frontend/public/dmn/list-types.dmn @@ -0,0 +1,42 @@ + + + BKMs and types that may be useful in any model. + + + Any + + + boolean + + + date + + + number + + + string + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/builder-frontend/public/vite.svg b/builder-frontend/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/builder-frontend/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/builder-frontend/src/App.css b/builder-frontend/src/App.css new file mode 100644 index 0000000..e69de29 diff --git a/builder-frontend/src/App.jsx b/builder-frontend/src/App.jsx new file mode 100644 index 0000000..95d3073 --- /dev/null +++ b/builder-frontend/src/App.jsx @@ -0,0 +1,28 @@ +import "./App.css"; +import { Route } from "@solidjs/router"; +import Project from "./components/project/Project"; +import ProjectsList from "./components/projectsList/ProjectsList"; +import AuthForm from "./components/auth/AuthForm"; +import { useAuth } from "./context/AuthContext"; + +function App() { + const { user } = useAuth(); + + return ( + <> + {user() === "loading" ? ( +
Loading...
+ ) : ( + <> + + + + + 404 - Page Not Found} /> + + )} + + ); +} + +export default App; diff --git a/builder-frontend/src/api/auth.js b/builder-frontend/src/api/auth.js new file mode 100644 index 0000000..6a51902 --- /dev/null +++ b/builder-frontend/src/api/auth.js @@ -0,0 +1,21 @@ +import { getAuth } from "firebase/auth"; + +export async function authFetch(input, init = {}) { + const auth = getAuth(); + const user = auth.currentUser; + + // If no user is logged in, you can handle it accordingly + if (!user) { + throw new Error("User not authenticated"); + } + + const token = await user.getIdToken(); + + const headers = new Headers(init.headers || {}); + headers.set("Authorization", `Bearer ${token}`); + + return fetch(input, { + ...init, + headers, + }); +} diff --git a/builder-frontend/src/api/models.js b/builder-frontend/src/api/models.js new file mode 100644 index 0000000..80c448b --- /dev/null +++ b/builder-frontend/src/api/models.js @@ -0,0 +1,46 @@ +import { authFetch } from "./auth"; +const apiUrl = import.meta.env.VITE_API_URL; + +export const fetchAvailableModels = async () => { + const url = apiUrl + "/dmn-models"; + try { + const response = await authFetch(url, { + method: "GET", + headers: { + Accept: "application/json", + }, + }); + + if (!response.ok) { + throw new Error(`Fetch failed with status: ${response.status}`); + } + const data = await response.json(); + return data; + } catch (error) { + console.error("Error fetching models:", error); + throw error; // rethrow so you can handle it in your component if needed + } +}; + +export const fetchModel = async (groupId, artifactId, version) => { + const url = apiUrl + `/dmn-models/${groupId}/${artifactId}/${version}`; + try { + const response = await authFetch(url, { + method: "GET", + headers: { + Accept: "application/json", + }, + }); + + if (!response.ok) { + throw new Error(`Fetch failed with status: ${response.status}`); + } + const data = await response.json(); + console.log("returned xml"); + console.log(data); + return data; + } catch (error) { + console.error("Error fetching model:", error); + throw error; // rethrow so you can handle it in your component if needed + } +}; diff --git a/builder-frontend/src/api/screener.js b/builder-frontend/src/api/screener.js new file mode 100644 index 0000000..ba67e27 --- /dev/null +++ b/builder-frontend/src/api/screener.js @@ -0,0 +1,245 @@ +import { authFetch } from "./auth"; +import { cloneDeep } from "lodash"; +const apiUrl = import.meta.env.VITE_API_URL; + +export const fetchProjects = async () => { + const url = apiUrl + "/screeners"; + try { + const response = await authFetch(url, { + method: "GET", + headers: { + Accept: "application/json", + }, + }); + + if (!response.ok) { + throw new Error(`Fetch failed with status: ${response.status}`); + } + const data = await response.json(); + return data; + } catch (error) { + console.error("Error fetching projects:", error); + throw error; // rethrow so you can handle it in your component if needed + } +}; + +export const fetchProject = async (screenerId) => { + const url = apiUrl + "/screener/" + screenerId; + try { + const response = await authFetch(url, { + method: "GET", + headers: { + Accept: "application/json", + }, + }); + + if (!response.ok) { + throw new Error(`Fetch failed with status: ${response.status}`); + } + const data = await response.json(); + return data; + } catch (error) { + console.error("Error fetching screener:", error); + throw error; // rethrow so you can handle it in your component if needed + } +}; + +export const createNewScreener = async (screenerData) => { + const url = apiUrl + "/screener"; + try { + const response = await authFetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(screenerData), + }); + + if (!response.ok) { + throw new Error(`Post failed with status: ${response.status}`); + } + const data = await response.json(); + return data; + } catch (error) { + console.error("Error creating new project:", error); + throw error; // rethrow so you can handle it in your component if needed + } +}; + +export const updateScreener = async (screenerData) => { + const url = apiUrl + "/screener"; + try { + const response = await authFetch(url, { + method: "PUT", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(screenerData), + }); + + if (!response.ok) { + throw new Error(`Update failed with status: ${response.status}`); + } + } catch (error) { + console.error("Error updating project:", error); + throw error; + } +}; + +export const deleteScreener = async (screenerData) => { + const url = apiUrl + "/screener/delete?screenerId=" + screenerData.id; + try { + const response = await authFetch(url, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + }); + + if (!response.ok) { + throw new Error(`Update failed with status: ${response.status}`); + } + } catch (error) { + console.error("Error updating project:", error); + throw error; + } +}; + +export const saveFormSchema = async (screenerId, schema) => { + const requestData = {}; + requestData.screenerId = screenerId; + requestData.schema = schema; + const url = apiUrl + "/save-form-schema"; + try { + const response = await authFetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(requestData), + }); + + if (!response.ok) { + throw new Error(`Post failed with status: ${response.status}`); + } + } catch (error) { + console.error("Error saving form schema:", error); + throw error; // rethrow so you can handle it in your component if needed + } +}; + +export const saveDmnModel = async (screenerId, dmnModel) => { + console.log(dmnModel); + const requestData = {}; + requestData.screenerId = screenerId; + requestData.dmnModel = dmnModel; + const url = apiUrl + "/save-dmn-model"; + try { + const response = await authFetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(requestData), + }); + + if (!response.ok) { + throw new Error(`Save failed with status: ${response.status}`); + } + } catch (error) { + console.error("Error saving DMN:", error); + throw error; // rethrow so you can handle it in your component if needed + } +}; + +export const submitForm = async (screenerId, data) => { + const url = apiUrl + "/decision?screenerId=" + screenerId; + const formData = cloneDeep(data); + + for (const key in formData) { + let value = formData[key]; + if (value === "true") { + formData[key] = true; + } else if (value === "false") { + formData[key] = false; + } + } + + if (!formData || Object.keys(formData).length === 0) return {}; + + try { + const response = await authFetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(formData), + }); + + if (!response.ok) { + throw new Error(`Submit failed with status: ${response.status}`); + } + const data = await response.json(); + return data; + } catch (error) { + console.error("Error submitting form:", error); + throw error; // rethrow so you can handle it in your component if needed + } +}; + +export const publishScreener = async (screenerId) => { + const url = apiUrl + "/publish"; + try { + const response = await authFetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify({ screenerId: screenerId }), + }); + + if (!response.ok) { + throw new Error(`Submit failed with status: ${response.status}`); + } + + const data = await response.json(); + return data; + } catch (error) { + console.error("Error submitting form:", error); + throw error; + } +}; + +export const addDependency = async (screenerId, dependency) => { + const url = apiUrl + "/dependency"; + console.log({ screenerId }); + try { + const response = await authFetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify({ + screenerId: screenerId, + groupId: dependency.groupId, + artifactId: dependency.artifactId, + version: dependency.version, + }), + }); + + if (!response.ok) { + throw new Error(`Model import failed with status: ${response.status}`); + } + } catch (error) { + console.error("Error adding dependency:", error); + throw error; + } +}; diff --git a/builder-frontend/src/assets/solid.svg b/builder-frontend/src/assets/solid.svg new file mode 100644 index 0000000..025aa30 --- /dev/null +++ b/builder-frontend/src/assets/solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/builder-frontend/src/components/Header.jsx b/builder-frontend/src/components/Header.jsx new file mode 100644 index 0000000..7b5717c --- /dev/null +++ b/builder-frontend/src/components/Header.jsx @@ -0,0 +1,36 @@ +import { useAuth } from "../context/AuthContext"; +import { useNavigate } from "@solidjs/router"; + +export default function Header() { + const { logout } = useAuth(); + const navigate = useNavigate(); + + const handleLogout = () => { + logout(); + navigate("/login", { replace: true }); + }; + + return ( +
+
+ + Benefits Decision Toolkit + +
+
+ navigate("/")} + class="font-bold text-sm text-gray-500 hover:underline" + > + ← Back to Projects + + + Logout + +
+
+ ); +} diff --git a/builder-frontend/src/components/Loading.jsx b/builder-frontend/src/components/Loading.jsx new file mode 100644 index 0000000..85e5d39 --- /dev/null +++ b/builder-frontend/src/components/Loading.jsx @@ -0,0 +1,10 @@ +export default function Loading() { + return ( +
+
+
Loading ...
+
+
+
+ ); +} diff --git a/builder-frontend/src/components/auth/AuthForm.jsx b/builder-frontend/src/components/auth/AuthForm.jsx new file mode 100644 index 0000000..744537a --- /dev/null +++ b/builder-frontend/src/components/auth/AuthForm.jsx @@ -0,0 +1,59 @@ +import { createSignal } from "solid-js"; +import GoogleLoginButton from "./GoogleLoginButton"; +import { useAuth } from "../../context/AuthContext"; +import Login from "./Login"; +import Signup from "./Signup"; +import { useLocation, useNavigate } from "@solidjs/router"; + +export default function AuthForm() { + const [isSigningIn, setIsSigningIn] = createSignal(false); + const { loginWithGoogle } = useAuth(); + const navigate = useNavigate(); + const location = useLocation(); + + const toggleMode = () => { + if (location.pathname === "/login") { + navigate("/signup"); + } else { + navigate("/login"); + } + }; + + const handleGoogleLogin = async () => { + try { + setIsSigningIn(true); + await loginWithGoogle(); + setIsSigningIn(false); + navigate("/"); + } catch (err) { + setIsSigningIn(false); + + console.error(err.message); + } + }; + + return ( +
+
+
+ Benefits Decision Tookit +
+ {location.pathname === "/login" ? ( + + ) : ( + + )} +
+
+
+ OR +
+
+ +
+
+ ); +} diff --git a/builder-frontend/src/components/auth/GoogleLoginButton.jsx b/builder-frontend/src/components/auth/GoogleLoginButton.jsx new file mode 100644 index 0000000..148d459 --- /dev/null +++ b/builder-frontend/src/components/auth/GoogleLoginButton.jsx @@ -0,0 +1,47 @@ +export default function GoogleLoginButton({ isSigningIn, onGoogleSignIn }) { + return ( + + ); +} diff --git a/builder-frontend/src/components/auth/Login.jsx b/builder-frontend/src/components/auth/Login.jsx new file mode 100644 index 0000000..af44fe2 --- /dev/null +++ b/builder-frontend/src/components/auth/Login.jsx @@ -0,0 +1,99 @@ +import { createSignal } from "solid-js"; +import { useAuth } from "../../context/AuthContext"; +import { useNavigate } from "@solidjs/router"; + +const firebaseErrorMessages = { + "auth/user-not-found": "No user found with this email.", + "auth/wrong-password": "Incorrect password.", + "auth/invalid-email": "Please enter a valid email address.", + "auth/missing-password": "Please enter your password.", + "auth/user-disabled": "This user account has been disabled.", + "auth/too-many-requests": "Too many failed attempts. Please try again later.", + "auth/weak-password": "Password must be at least 6 characters.", + // "auth/invalid-credential": "Click sign Up to create a new account" + // Add more as needed +}; + +function getFriendlyErrorMessage(firebaseError) { + return ( + firebaseErrorMessages[firebaseError.code] || "Incorrect email or password." + ); +} + +export default function Login({ toggleMode }) { + const [email, setEmail] = createSignal(""); + const [password, setPassword] = createSignal(""); + const [error, setError] = createSignal(""); + const [isSigningIn, setIsSigningIn] = createSignal(false); + const { loginWithGoogle, login } = useAuth(); + const navigate = useNavigate(); + + const handleLogin = async (e) => { + e.preventDefault(); + try { + setIsSigningIn(true); + await login(email(), password()); + setIsSigningIn(false); + setError(null); + navigate("/", { replace: true }); + } catch (err) { + setError(getFriendlyErrorMessage(err)); + setIsSigningIn(false); + console.error(err.message); + } + }; + + return ( +
+
+ + setEmail(e.target.value)} + /> +
+
+ + setPassword(e.target.value)} + /> +
+ + + +
+ Don't have an account?  + + Sign Up + +
+
+ {error() &&

{error()}

} +
+
+ ); +} diff --git a/builder-frontend/src/components/auth/ProtectedRoute.jsx b/builder-frontend/src/components/auth/ProtectedRoute.jsx new file mode 100644 index 0000000..69f487e --- /dev/null +++ b/builder-frontend/src/components/auth/ProtectedRoute.jsx @@ -0,0 +1,10 @@ +// src/components/ProtectedRoute.jsx +import { useAuth } from "../../context/AuthContext"; +import { Navigate } from "@solidjs/router"; + +export default function ProtectedRoute(props) { + const { user } = useAuth(); + console.log("rendering protected route. user:"); + console.log(user()); + return user() ? props.children : ; +} diff --git a/builder-frontend/src/components/auth/Signup.jsx b/builder-frontend/src/components/auth/Signup.jsx new file mode 100644 index 0000000..b4e6795 --- /dev/null +++ b/builder-frontend/src/components/auth/Signup.jsx @@ -0,0 +1,125 @@ +import { createSignal } from "solid-js"; +import { useAuth } from "../../context/AuthContext"; +import { useNavigate } from "@solidjs/router"; + +const firebaseErrorMessages = { + "auth/user-not-found": "No user found with this email.", + "auth/wrong-password": "Incorrect password.", + "auth/invalid-email": "Please enter a valid email address.", + "auth/missing-password": "Please enter your password.", + "auth/user-disabled": "This user account has been disabled.", + "auth/too-many-requests": "Too many failed attempts. Please try again later.", + "auth/weak-password": "Password must be at least 6 characters.", + "auth/email-already-in-use": + "Email is already associated with a registered user.", + // "auth/invalid-credential": "Click sign Up to create a new account" + // Add more as needed +}; + +function getFriendlyErrorMessage(firebaseError) { + return ( + firebaseErrorMessages[firebaseError.code] || + "Login failed. Please try again." + ); +} + +export default function Signup({ toggleMode }) { + const [email, setEmail] = createSignal(""); + const [password, setPassword] = createSignal(""); + const [passwordConfirm, setPasswordConfirm] = createSignal(""); + const [error, setError] = createSignal(""); + const [isSigningIn, setIsSigningIn] = createSignal(false); + const { loginWithGoogle, register } = useAuth(); + const navigate = useNavigate(); + + const handleRegister = async (e) => { + e.preventDefault(); + const isInputValid = validateInput(); + if (!isInputValid) return; + + try { + setIsSigningIn(true); + await register(email(), password()); + setIsSigningIn(false); + setError(null); + navigate("/", { replace: true }); + } catch (err) { + setError(getFriendlyErrorMessage(err)); + setIsSigningIn(false); + console.error(err.message); + } + }; + + const validateInput = () => { + if (password() != passwordConfirm()) { + setError("Password does not match."); + return false; + } + return true; + }; + + return ( +
+
+ + setEmail(e.target.value)} + /> +
+
+ + setPassword(e.target.value)} + /> +
+
+ + setPasswordConfirm(e.target.value)} + /> +
+ +
+ Have an account?  + + Sign In + +
+
+ {error() &&

{error()}

} +
+
+ ); +} diff --git a/builder-frontend/src/components/icon/MenuIcon.jsx b/builder-frontend/src/components/icon/MenuIcon.jsx new file mode 100644 index 0000000..5a349a6 --- /dev/null +++ b/builder-frontend/src/components/icon/MenuIcon.jsx @@ -0,0 +1,24 @@ +export default function MenuIcon() { + return ( + + + + + + ); +} diff --git a/builder-frontend/src/components/icon/TrashIcon.jsx b/builder-frontend/src/components/icon/TrashIcon.jsx new file mode 100644 index 0000000..4bd6132 --- /dev/null +++ b/builder-frontend/src/components/icon/TrashIcon.jsx @@ -0,0 +1,19 @@ +export default function TrashIcon() { + return ( + + + + ); +} diff --git a/builder-frontend/src/components/project/FormEditorView.jsx b/builder-frontend/src/components/project/FormEditorView.jsx new file mode 100644 index 0000000..2bd5483 --- /dev/null +++ b/builder-frontend/src/components/project/FormEditorView.jsx @@ -0,0 +1,90 @@ +import { onMount, onCleanup, createSignal } from "solid-js"; +import { FormEditor } from "@bpmn-io/form-js-editor"; +import { saveFormSchema } from "../../api/screener"; +import { useParams } from "@solidjs/router"; +import "@bpmn-io/form-js/dist/assets/form-js.css"; +import "@bpmn-io/form-js-editor/dist/assets/form-js-editor.css"; + +function FormEditorView({ formSchema, setFormSchema }) { + const [isUnsaved, setIsUnsaved] = createSignal(false); + const [isSaving, setIsSaving] = createSignal(false); + const params = useParams(); + + let timeoutId; + let container; + let formEditor; + let emptySchema = { + components: [], + exporter: { name: "form-js (https://demo.bpmn.io)", version: "1.15.0" }, + id: "Form_1sgem74", + schemaVersion: 18, + type: "default", + }; + + onMount(() => { + formEditor = new FormEditor({ container }); + + if (formSchema()) { + formEditor.importSchema(formSchema()).catch((err) => { + console.error("Failed to load schema", err); + }); + } else { + formEditor.importSchema(emptySchema).catch((err) => { + console.error("Failed to load schema", err); + }); + } + + formEditor.on("changed", (e) => { + setIsUnsaved(true); + setFormSchema(e.schema); + }); + + onCleanup(() => { + if (formEditor) { + formEditor.destroy(); + formEditor = null; + clearTimeout(timeoutId); + } + }); + }); + + const handleSave = async () => { + console.log("save"); + const projectId = params.projectId; + const schema = formSchema(); + setIsUnsaved(false); + setIsSaving(true); + saveFormSchema(projectId, schema); + clearTimeout(timeoutId); + timeoutId = setTimeout(() => setIsSaving(false), 500); + }; + + return ( + <> +
+
(container = el)} /> +
+ +
+ {isUnsaved() && ( + + unsaved changes + + )} + {isSaving() && ( + + saving ... + + )} + +
+ + ); +} + +export default FormEditorView; diff --git a/builder-frontend/src/components/project/FormRenderer.jsx b/builder-frontend/src/components/project/FormRenderer.jsx new file mode 100644 index 0000000..f33f660 --- /dev/null +++ b/builder-frontend/src/components/project/FormRenderer.jsx @@ -0,0 +1,32 @@ +import { onMount } from "solid-js"; +import { Form } from "@bpmn-io/form-js-viewer"; +import debounce from "lodash.debounce"; +import "@bpmn-io/form-js/dist/assets/form-js.css"; + +function FormRenderer({ schema, submitForm }) { + let container; + + onMount(() => { + const form = new Form({ container }); + const debouncedSubmit = debounce((data) => { + submitForm(data); + }, 1000); + + form + .importSchema(schema) + .then(() => { + form.on("changed", (event) => { + debouncedSubmit(event.data); + }); + }) + .catch(console.error); + }); + + return ( +
+
(container = el)} /> +
+ ); +} + +export default FormRenderer; diff --git a/builder-frontend/src/components/project/ImportModels.jsx b/builder-frontend/src/components/project/ImportModels.jsx new file mode 100644 index 0000000..e2f0896 --- /dev/null +++ b/builder-frontend/src/components/project/ImportModels.jsx @@ -0,0 +1,104 @@ +import { createResource, createSignal, createMemo } from "solid-js"; +import { fetchAvailableModels } from "../../api/models"; +import ModelDetail from "./ModelDetail"; +import { addDependency } from "../../api/screener"; + +export default function ImportModels({ + screener, + dependencies, + fetchAndCacheProject, +}) { + const [availableModels, { refetchAvailableModels }] = + createResource(fetchAvailableModels); + const [selectedModel, setSelectedModel] = createSignal(); + const [showInstalled, setShowInstalled] = createSignal(false); + + const installedModels = createMemo(() => { + if (!dependencies) return []; + const depKeys = new Set( + dependencies.map( + (dep) => `${dep.groupId}:${dep.artifactId}:${dep.version}` + ) + ); + console.log({ depKeys }); + if (!availableModels()) return []; + return availableModels().filter((model) => + depKeys.has(`${model.groupId}:${model.artifactId}:${model.version}`) + ); + }); + + const handleImportModel = async (model) => { + await addDependency(screener().id, model); + await fetchAndCacheProject(screener().id); + }; + + const models = createMemo(() => { + return showInstalled() ? installedModels() : availableModels(); + }); + + return ( +
+ {/* Left Pane: Scrollable list */} +
+ {/* Available Models Header */} +
+ Available Models +
+ All + + + + Installed +
+
+ {models() && + models().map((model, index) => ( +
setSelectedModel(model)} + key={index} + className="p-4 border-b border-gray-200 cursor-pointer hover:bg-gray-100" + > +
{model.name}
+
+ {model.shortDescription} +
+
{model.groupId}
+
+ ))} +
+ {/* Model Details window */} +
+ {!!selectedModel() ? ( + console.log("On remove")} + deselect={() => setSelectedModel()} + > + ) : ( + <> +
+ Include external models in your screener project. +
+
+ Import pre-built models to use in your project. When you import an + external DMN model, it's elements are avilable to use in the DMN + editor. DMN decisions and other elements from an imported model + cannot be modified. +
+ + )} +
+
+ ); +} diff --git a/builder-frontend/src/components/project/KogitoDmnEditorView.jsx b/builder-frontend/src/components/project/KogitoDmnEditorView.jsx new file mode 100644 index 0000000..f8a25f0 --- /dev/null +++ b/builder-frontend/src/components/project/KogitoDmnEditorView.jsx @@ -0,0 +1,130 @@ +import * as DmnEditor from "@kogito-tooling/kie-editors-standalone/dist/dmn"; +import { createSignal, onCleanup, onMount } from "solid-js"; +import { useParams } from "@solidjs/router"; +import { saveDmnModel } from "../../api/screener"; + +export default function KogitoDmnEditorView({ + dmnModel, + setDmnModel, + projectDependencies, +}) { + const [isUnsaved, setIsUnsaved] = createSignal("Not Dirty"); + const [isSaving, setIsSaving] = createSignal(false); + const params = useParams(); + let container; + let editor; + let timeoutId; + + const trimDmn = (xml) => { + let dmn = ""; + const firstChar = xml.charAt(0); + const lastChar = xml.charAt(xml.length - 1); + if (firstChar == '"' && lastChar == '"') dmn = xml.slice(1, -1); + else dmn = xml; + return dmn; + }; + + function buildDmnResources() { + console.log(projectDependencies()); + return new Map( + projectDependencies().map((dep) => [ + `${dep.groupId}:${dep.artifactId}:${dep.version}.dmn`, + { + contentType: "text", + content: Promise.resolve(trimDmn(dep.xml)), + }, + ]) + ); + } + + async function loadDependencies() { + const dependencies = new Map([ + "utility.dmn", + { + contentType: "text", + content: Promise.resolve(projectDependencies()[0].xml), + }, + ]); + + return dependencies; + } + + async function loadScreenerModel() { + let initialDmn = ""; + let xml = dmnModel(); + if (!xml) return initialDmn; + else { + const firstChar = xml.charAt(0); + const lastChar = xml.charAt(xml.length - 1); + if (firstChar == '"' && lastChar == '"') initialDmn = xml.slice(1, -1); + else initialDmn = xml; + } + + return initialDmn; + } + + const initializeEditor = async () => { + const initialDmn = loadScreenerModel(); + + editor = DmnEditor.open({ + container: container, + initialFileNormalizedPosixPathRelativeToTheWorkspaceRoot: "screener.dmn", + initialContent: Promise.resolve(initialDmn), + resources: buildDmnResources(), + readOnly: false, + }); + + editor.subscribeToContentChanges(async (isDirty) => { + if (isDirty) { + setIsUnsaved("Dirty"); + } else { + setIsUnsaved("Not Dirty"); + } + }); + }; + + onMount(async () => { + initializeEditor(); + }); + + onCleanup(() => { + if (editor) editor.close(); + }); + + const handleSave = async () => { + const xml = await editor.getContent(); + setIsUnsaved(false); + setIsSaving(true); + saveDmnModel(params.projectId, xml); + setDmnModel(xml); + setIsSaving(false); + clearTimeout(timeoutId); + timeoutId = setTimeout(() => setIsSaving(false), 500); + }; + + return ( + <> +
+
(container = el)} /> +
+
+ {isUnsaved() && ( + + unsaved changes + + )} + {isSaving() && ( + + saving ... + + )} + +
+ + ); +} diff --git a/builder-frontend/src/components/project/ModelDetail.jsx b/builder-frontend/src/components/project/ModelDetail.jsx new file mode 100644 index 0000000..000366d --- /dev/null +++ b/builder-frontend/src/components/project/ModelDetail.jsx @@ -0,0 +1,44 @@ +import { Show } from "solid-js"; + +export default function ModelDetail({ + model, + isImported, + onImport, + onRemove, + deselect, +}) { + return ( +
+
+
+ X +
+
+
+

{model.name}

+

Publisher: {model.groupId}

+

{model.description}

+
+
+ {isImported ? ( +
onRemove(model)} + className="px-2 rounded border-2 border-gray-500 text-gray-500 w-fit hover:bg-gray-100" + > + Remove Model +
+ ) : ( +
onImport(model)} + className="px-2 rounded border-2 border-emerald-500 text-emerald-500 w-fit hover:bg-emerald-100" + > + Import Model +
+ )} +
+
+ ); +} diff --git a/builder-frontend/src/components/project/Preview.jsx b/builder-frontend/src/components/project/Preview.jsx new file mode 100644 index 0000000..b64a812 --- /dev/null +++ b/builder-frontend/src/components/project/Preview.jsx @@ -0,0 +1,39 @@ +import { createSignal } from "solid-js"; +import { useParams } from "@solidjs/router"; +import { submitForm } from "../../api/screener"; +import FormRenderer from "./FormRenderer"; +import Results from "./Results"; + +export default function Preview({ formSchema }) { + const [results, setResults] = createSignal(); + const params = useParams(); + let schema = formSchema(); + if (!schema) { + schema = { + components: [], + exporter: { name: "form-js (https://demo.bpmn.io)", version: "1.15.0" }, + id: "Form_1sgem74", + schemaVersion: 18, + type: "default", + }; + } + + const handleSubmitForm = async (data) => { + let results = await submitForm(params.projectId, data); + setResults(results); + }; + + return ( + <> +
+ +
+ +
+
+ + ); +} diff --git a/builder-frontend/src/components/project/Project.jsx b/builder-frontend/src/components/project/Project.jsx new file mode 100644 index 0000000..653866d --- /dev/null +++ b/builder-frontend/src/components/project/Project.jsx @@ -0,0 +1,140 @@ +import { createSignal, onMount, createResource, Show } from "solid-js"; +import { useParams } from "@solidjs/router"; +import { + cacheDependency, + getCachedDependency, +} from "../../storageUtils/storageUtils"; +import "../../App.css"; +import Header from "../Header"; +import FormEditorView from "./FormEditorView"; +import Preview from "./Preview"; +import Publish from "./Publish"; +import KogitoDmnEditorView from "./KogitoDmnEditorView"; +import ImportModels from "./ImportModels"; +import { fetchProject } from "../../api/screener"; +import Loading from "../Loading"; +import { fetchModel } from "../../api/models"; + +function Project({ clearUserState }) { + const params = useParams(); + const [activeTab, setActiveTab] = createSignal("DMN Editor"); + const [dmnModel, setDmnModel] = createSignal(); + const [formSchema, setFormSchema] = createSignal(); + const [projectDependencies, setProjectDependencies] = createSignal([]); + + const fetchAndCacheProject = async (screenerId) => { + const projectData = await fetchProject(screenerId); + setDmnModel(projectData.dmnModel); + setFormSchema(projectData.formSchema); + fetchAndCacheProjectDependencies(projectData); + return projectData; + }; + + const fetchAndCacheProjectDependencies = async (screenerData) => { + if (screenerData.dependencies) { + // Read any cached dependencies from session storage and track any deps not cached + const cachedDeps = []; + const neededDeps = []; + for (const dep of screenerData.dependencies) { + const cachedDep = getCachedDependency(dep); + if (cachedDep) { + cachedDeps.push({ + groupId: dep.groupId, + artifactId: dep.artifactId, + version: dep.version, + xml: cachedDep, + }); + } else { + neededDeps.push(dep); + } + } + + //fetch any dependencies that are not cached + const dependencyPromises = []; + for (const dep of neededDeps) { + dependencyPromises.push( + await fetchModel(dep.groupId, dep.artifactId, dep.version) + ); + } + + const dependencies = await Promise.all(dependencyPromises); + for (const dep of dependencies) { + cacheDependency(dep); + cachedDeps.push(dep); + } + + setProjectDependencies(cachedDeps); + } + }; + + const [project, { refetchProject }] = createResource( + params.projectId, + fetchAndCacheProject + ); + + const handleSelectTab = (tab) => { + setActiveTab(tab); + }; + + return ( +
+
+ {project.loading ? ( + + ) : ( + <> +
+ + {" "} + {project().screenerName} + + {[ + "DMN Editor", + "Import Models", + "Form Editor", + "Preview", + "Publish", + ].map((tab) => ( + + ))} +
+ {activeTab() == "Form Editor" && ( + + )} + {activeTab() == "DMN Editor" && ( + + )} + {activeTab() == "Import Models" && ( + + )} + {activeTab() == "Preview" && ( + + )} + {activeTab() == "Publish" && } + + )} +
+ ); +} + +export default Project; diff --git a/builder-frontend/src/components/project/Publish.jsx b/builder-frontend/src/components/project/Publish.jsx new file mode 100644 index 0000000..b69e466 --- /dev/null +++ b/builder-frontend/src/components/project/Publish.jsx @@ -0,0 +1,88 @@ +import { createSignal, onMount } from "solid-js"; +import { useParams } from "@solidjs/router"; +import { publishScreener, fetchProject } from "../../api/screener"; +const screenerBaseUrl = import.meta.env.VITE_SCREENER_BASE_URL; +export default function Publish({ project }) { + const [isLoading, setIsLoading] = createSignal(false); + const [screenerName, setScreenerName] = createSignal(); + const [isPublished, setIsPublished] = createSignal(); + const [lastPublishDate, setLastPublishDate] = createSignal(); + const [screenerUrl, setScreenerUrl] = createSignal(); + const { projectId } = useParams(); + + onMount(() => { + setScreenerState(); + }); + + const setScreenerState = () => { + if (!project()) return; + setScreenerName(project().name); + setIsPublished(project().published); + setLastPublishDate(project().lastPublishDate); + setScreenerUrl(screenerBaseUrl + "screener/" + project().id); + }; + + const handlePublish = async () => { + try { + setIsLoading(true); + await publishScreener(projectId); + const screenerData = await fetchProject(projectId); + setScreenerState(); + setIsLoading(false); + } catch (e) { + setIsLoading(false); + } + }; + + const formattedDate = (isoString) => { + const trimmed = isoString.replace(/\.\d{3,}Z$/, "Z"); + const date = new Date(trimmed); + + return new Intl.DateTimeFormat("en-US", { + dateStyle: "medium", + timeStyle: "short", + }).format(date); + }; + + return ( +
+
+
{screenerName()}
+
+
+
Screener URL:
+ {isPublished() ? ( + + {screenerUrl()} + + ) : ( + Deploy screener to create public url. + )} +
+
+
Last Published Date:
+ {lastPublishDate() ? ( +
{formattedDate(lastPublishDate())}
+ ) : ( +
Not yet published
+ )} +
+
+
+ + {lastPublishDate() ? ( +
Deploy current working version to your public screener
+ ) : ( +
Click to make your screener availble through a public URL
+ )} +
+
+
+ ); +} diff --git a/builder-frontend/src/components/project/Results.jsx b/builder-frontend/src/components/project/Results.jsx new file mode 100644 index 0000000..c9bbfcf --- /dev/null +++ b/builder-frontend/src/components/project/Results.jsx @@ -0,0 +1,63 @@ +import { result } from "lodash"; +import { For, onMount } from "solid-js"; + +export default function Results({ results }) { + return ( +
+
Results
+
+ {results() && results().inputs && ( + <> +
Inputs
+ +
+
+ + {([key, value]) => ( +
+ {key}: + {value?.toString()} +
+ )} +
+
+
+ + )} + {results() && results().decisions && ( + <> +
Decisions
+ + + {(item) => + item && ( +
+
+ + {([key, value]) => ( +
+ {key}: + {value?.toString()} +
+ )} +
+
+
+ ) + } +
+ + )} + {/* Messages Section */} + {results() && results().messages && results().messages.length > 0 && ( +
+
Messages
+
    + {(msg) =>
  • {msg}
  • }
    +
+
+ )} +
+
+ ); +} diff --git a/builder-frontend/src/components/projectsList/DeleteConfirmation.jsx b/builder-frontend/src/components/projectsList/DeleteConfirmation.jsx new file mode 100644 index 0000000..9cd2c79 --- /dev/null +++ b/builder-frontend/src/components/projectsList/DeleteConfirmation.jsx @@ -0,0 +1,41 @@ +import TrashIcon from "../icon/TrashIcon"; + +export default function DeleteConfirmation({ + screenerData, + setIsConfirmationVisible, + handleDelete, +}) { + return ( +
setIsConfirmationVisible(false)} + className="fixed inset-0 bg-black/10 flex items-center justify-center z-100" + > +
e.stopPropagation()} + className="bg-white px-12 py-8 rounded-xl max-w-100 w-1/2 min-w-80 h-80" + > +
+ Are you sure you would like to delete {screenerData.screenerName}? +
+
+ Once deleted, all associated data will be deleted and cant be + recovered. +
+
+ + +
+
+
+ ); +} diff --git a/builder-frontend/src/components/projectsList/EditScreenerForm.jsx b/builder-frontend/src/components/projectsList/EditScreenerForm.jsx new file mode 100644 index 0000000..71256ca --- /dev/null +++ b/builder-frontend/src/components/projectsList/EditScreenerForm.jsx @@ -0,0 +1,125 @@ +import { createSignal, onCleanup, onMount } from "solid-js"; +import TrashIcon from "../icon/TrashIcon"; +import DeleteConfirmation from "./DeleteConfirmation"; + +export default function EditScreenerForm({ + setIsEditModalVisible, + screenerData, + handleEditScreener, + handleDeleteScreener, +}) { + const [isLoading, setIsLoading] = createSignal(false); + const [isConfirmationVisible, setIsConfirmationVisible] = createSignal(false); + const [screenerName, setScreenerName] = createSignal(); + let isActive = true; + + onMount(() => { + if (screenerData?.screenerName) { + setScreenerName(screenerData.screenerName); + } + }); + + onCleanup(() => { + isActive = false; + }); + + const handleSubmit = async (e) => { + e.preventDefault(); + try { + setIsLoading(true); + const data = { + screenerName: screenerName(), + id: screenerData.id, + }; + await handleEditScreener(data); + if (isActive) setIsLoading(false); + } catch (e) { + if (setIsLoading()) { + setIsLoading(false); + } + } + }; + + const handleDelete = async () => { + try { + setIsLoading(true); + const data = { + id: screenerData.id, + }; + await handleDeleteScreener(data); + if (isActive) setIsLoading(false); + } catch (e) { + if (setIsLoading()) { + setIsLoading(false); + } + } + }; + + const handleDeleteClicked = (e) => { + e.preventDefault(); + setIsConfirmationVisible(true); + }; + + return ( + <> +
setIsEditModalVisible(false)} + className="fixed inset-0 bg-black/10 backdrop-blur-sm flex items-center justify-center z-50" + > +
e.stopPropagation()} + className="bg-white px-12 py-8 rounded-xl max-w-140 w-1/2 min-w-80 h-96" + > +
+
Edit screener
+
setIsEditModalVisible(false)} + className="text-2xl hover:font-bold hover:cursor-pointer" + > + X +
+
+ +
+
+ + setScreenerName(e.currentTarget.value)} + className="p-1 border-1 border-gray-400 w-90" + > +
+ + +
+
+
+ + + + {isLoading() &&
Loading ...
} +
+
+
+ {isConfirmationVisible() && ( + + )} + + ); +} diff --git a/builder-frontend/src/components/projectsList/NewScreenerForm.jsx b/builder-frontend/src/components/projectsList/NewScreenerForm.jsx new file mode 100644 index 0000000..f781a3e --- /dev/null +++ b/builder-frontend/src/components/projectsList/NewScreenerForm.jsx @@ -0,0 +1,67 @@ +import { createSignal, onCleanup } from "solid-js"; + +export default function NewScreenerForm({ + setIsModalVisible, + handleCreateNewScreener, +}) { + const [isLoading, setIsLoading] = createSignal(false); + let nameInput; + let isActive = true; + + onCleanup(() => { + isActive = false; + }); + + const handleSubmit = async (e) => { + e.preventDefault(); + setIsLoading(true); + const data = { + screenerName: nameInput.value, + }; + await handleCreateNewScreener(data); + if (isActive) setIsLoading(false); + }; + + return ( +
setIsModalVisible(false)} + className="fixed inset-0 bg-black/10 backdrop-blur-sm flex items-center justify-center z-50" + > +
e.stopPropagation()} + className="bg-white px-12 py-8 rounded-xl max-w-140 w-1/2 min-w-80 h-96" + > +
+
Create a screener
+
setIsModalVisible(false)} + className="text-2xl hover:font-bold hover:cursor-pointer" + > + X +
+
+
+ What is the name of your screener? +
+
+
+ + (nameInput = el)} + className="p-1 border-1 border-gray-400 w-90" + > +
+ + {isLoading() &&
Loading ...
} +
+
+
+ ); +} diff --git a/builder-frontend/src/components/projectsList/ProjectsList.jsx b/builder-frontend/src/components/projectsList/ProjectsList.jsx new file mode 100644 index 0000000..fccce88 --- /dev/null +++ b/builder-frontend/src/components/projectsList/ProjectsList.jsx @@ -0,0 +1,123 @@ +import { For, createResource, createSignal, onMount } from "solid-js"; +import { useNavigate } from "@solidjs/router"; +import Header from "../Header"; +import { + fetchProjects, + updateScreener, + deleteScreener, + createNewScreener, +} from "../../api/screener"; +import NewScreenerForm from "./NewScreenerForm"; +import MenuIcon from "../icon/MenuIcon"; +import EditScreenerForm from "./EditScreenerForm"; +import { useAuth } from "../../context/AuthContext"; +export default function ProjectsList({ clearUserState }) { + const [data, { refetch }] = createResource(fetchProjects); + const [isNewScreenerModalVisible, setIsNewScreenerModalVisible] = + createSignal(false); + const [isEditModalVisible, setIsEditgModalVisible] = createSignal(false); + const [editModelData, setEditModalData] = createSignal(); + const navigate = useNavigate(); + const { user } = useAuth(); + + onMount(() => { + if (user() === null) { + navigate("/login", { replace: true }); + } + }); + + const navigateToProject = (project) => { + navigate("/project/" + project.id); + }; + + const handleCreateNewScreener = async (screenerData) => { + try { + const newScreener = await createNewScreener(screenerData); + navigate(`/project/${newScreener.id}`); + } catch (e) { + console.log("Error creating screener", e); + } + }; + + const handleProjectMenuClicked = (e, screenerData) => { + e.stopPropagation(); + setEditModalData(screenerData); + setIsEditgModalVisible(true); + }; + + const handleUpdateScreener = async (screenerData) => { + try { + await updateScreener(screenerData); + refetch(); + setIsEditgModalVisible(false); + } catch (e) { + console.log("Error editing screener", e); + } + }; + + const handleDeleteScreener = async (screenerData) => { + try { + await deleteScreener(screenerData); + refetch(); + setIsEditgModalVisible(false); + } catch (e) { + console.log("Error deleting screener", e); + } + }; + + return ( + <> +
+
+ Loading...
}> +
+
setIsNewScreenerModalVisible(true)} + class="rounded-lg p-4 w-80 h-60 flex justify-center border-6 shadow-md border-gray-300 hover:shadow-lg hover:bg-gray-100" + > +
+ Create new screener +
+
+ + {(item) => + item && ( +
navigateToProject(item)} + class="rounded-lg p-4 w-80 h-60 border-1 shadow-md border-gray-300 hover:shadow-lg hover:bg-gray-100" + > +
+
+ {item.screenerName} +
+
handleProjectMenuClicked(e, item)} + > + +
+
+
+ ) + } +
+
+ +
+ {isNewScreenerModalVisible() && ( + + )} + {isEditModalVisible() && ( + + )} + + ); +} diff --git a/builder-frontend/src/context/AuthContext.jsx b/builder-frontend/src/context/AuthContext.jsx new file mode 100644 index 0000000..f102a08 --- /dev/null +++ b/builder-frontend/src/context/AuthContext.jsx @@ -0,0 +1,71 @@ +import { + createSignal, + createContext, + useContext, + onCleanup, + onMount, +} from "solid-js"; +import { + onAuthStateChanged, + signOut, + signInWithEmailAndPassword, + createUserWithEmailAndPassword, + signInWithPopup, + GoogleAuthProvider, + signInWithRedirect, +} from "firebase/auth"; +import { auth } from "../firebase/firebase"; + +const AuthContext = createContext(); +const googleProvider = new GoogleAuthProvider(); + +export function AuthProvider(props) { + const [user, setUser] = createSignal("loading"); + const [isAuthLoading, setIsAuthLoading] = createSignal(true); + let unsubscribe; + + onMount(() => { + unsubscribe = onAuthStateChanged(auth, (firebaseUser) => { + setIsAuthLoading(true); + setUser(firebaseUser); + setIsAuthLoading(false); + }); + }); + + onCleanup(() => { + if (unsubscribe) unsubscribe(); + }); + + const login = async (email, password) => { + console.log("Loging in"); + return signInWithEmailAndPassword(auth, email, password); + }; + + const register = async (email, password) => { + return createUserWithEmailAndPassword(auth, email, password); + }; + + const loginWithGoogle = async () => { + try { + return signInWithPopup(auth, googleProvider); + } catch (error) { + console.error("Google sign-in error:", error.message); + } + }; + + const logout = async () => { + await signOut(auth); + }; + + return ( + + {props.children} + + ); +} + +export function useAuth() { + return useContext(AuthContext); +} diff --git a/builder-frontend/src/firebase/firebase.js b/builder-frontend/src/firebase/firebase.js new file mode 100644 index 0000000..f616f8d --- /dev/null +++ b/builder-frontend/src/firebase/firebase.js @@ -0,0 +1,25 @@ +import { initializeApp } from "firebase/app"; +import { getAnalytics } from "firebase/analytics"; +import { getAuth } from "firebase/auth"; +const API_KEY = import.meta.env.VITE_API_KEY; +const AUTH_DOMAIN = import.meta.env.VITE_AUTH_DOMAIN; +const PROJECT_ID = import.meta.env.VITE_PROJECT_ID; +const STORAGE_BUCKET = import.meta.env.VITE_STORAGE_BUCKET; +const MESSAGING_SENDER_ID = import.meta.env.VITE_MESSAGING_SENDER_ID; +const APP_ID = import.meta.env.VITE_APP_ID; +const MEASUREMENT_ID = import.meta.env.VITE_MEASUREMENT_ID; + +const firebaseConfig = { + apiKey: API_KEY, + authDomain: AUTH_DOMAIN, + projectId: PROJECT_ID, + storageBucket: STORAGE_BUCKET, + messagingSenderId: MESSAGING_SENDER_ID, + appId: APP_ID, + measurementId: MEASUREMENT_ID, +}; + +// Initialize Firebase +const app = initializeApp(firebaseConfig); +const analytics = getAnalytics(app); +export const auth = getAuth(app); diff --git a/builder-frontend/src/index.css b/builder-frontend/src/index.css new file mode 100644 index 0000000..4765f8d --- /dev/null +++ b/builder-frontend/src/index.css @@ -0,0 +1,13 @@ +@import "tailwindcss"; + +.fjs-powered-by { + display: none; +} + +.bjs-powered-by { + display: none; +} + +body { + font-family: "IBM Plex Sans", sans-serif; +} diff --git a/builder-frontend/src/index.jsx b/builder-frontend/src/index.jsx new file mode 100644 index 0000000..c9f8bbb --- /dev/null +++ b/builder-frontend/src/index.jsx @@ -0,0 +1,19 @@ +/* @refresh reload */ +import { render } from "solid-js/web"; +import "./index.css"; +import App from "./App.jsx"; +import { AuthProvider } from "./context/AuthContext.jsx"; +import { Router } from "@solidjs/router"; + +const root = document.getElementById("root"); + +render( + () => ( + + + + + + ), + root +); diff --git a/builder-frontend/src/storageUtils/storageUtils.js b/builder-frontend/src/storageUtils/storageUtils.js new file mode 100644 index 0000000..86ec7ff --- /dev/null +++ b/builder-frontend/src/storageUtils/storageUtils.js @@ -0,0 +1,17 @@ +export const StorageKeys = { + SELECTED_PROJECT: "selectedProject", + FORM_SCHEMA: "formSchema", + DMN_MODEL: "dmnModel", + TAB: "tab", +}; + +export const getCachedDependency = (dep) => { + const key = `${dep.groupId}:${dep.artifactId}:${dep.version}`; + return sessionStorage.getItem(key); +}; + +export const cacheDependency = (dep) => { + console.log("cache dep"); + const key = `${dep.groupId}:${dep.artifactId}:${dep.version}`; + return sessionStorage.setItem(key, dep.xml); +}; diff --git a/builder-frontend/src/testForm.json b/builder-frontend/src/testForm.json new file mode 100644 index 0000000..e4bb1e3 --- /dev/null +++ b/builder-frontend/src/testForm.json @@ -0,0 +1,60 @@ +{ + "components": [ + { + "text": "Philly Tax Benefit", + "type": "text", + "layout": { + "row": "Row_1y05iqm", + "columns": null + }, + "id": "Field_1wvy7sr" + }, + { + "label": "Season", + "values": [ + { + "label": "Value", + "value": "value" + }, + { + "label": "Summer", + "value": "summer" + }, + { + "label": "Winter", + "value": "winter" + } + ], + "type": "select", + "layout": { + "row": "Row_1opwfd7", + "columns": null + }, + "id": "Field_0yalsxi", + "key": "select_7qzimg" + }, + { + "label": "Checkbox group", + "values": [ + { + "label": "Do you live in philly?", + "value": "value" + } + ], + "type": "checklist", + "layout": { + "row": "Row_0hwvoae", + "columns": null + }, + "id": "Field_1ckvy4y", + "key": "checklist_3zsdrj" + } + ], + "type": "default", + "id": "Form_1sgem74", + "exporter": { + "name": "form-js (https://demo.bpmn.io)", + "version": "1.15.0" + }, + "schemaVersion": 18 +} \ No newline at end of file diff --git a/builder-frontend/src/utils/dmn-utils.js b/builder-frontend/src/utils/dmn-utils.js new file mode 100644 index 0000000..6f5ec54 --- /dev/null +++ b/builder-frontend/src/utils/dmn-utils.js @@ -0,0 +1,21 @@ +export function insertImportElement(dmnXmlString, importElementsXml) { + const definitionsTagRegex = /]*?>/is; + const match = dmnXmlString.match(definitionsTagRegex); + + if (!match) { + console.log("Error: Malformed DMN file"); + return; + } + + const openTag = match[0]; + const insertIndex = dmnXmlString.indexOf(openTag) + openTag.length; + + // Insert import elements right after + return ( + dmnXmlString.slice(0, insertIndex) + + "\n" + + importElementsXml + + "\n" + + dmnXmlString.slice(insertIndex) + ); +} diff --git a/builder-frontend/tailwind.config.js b/builder-frontend/tailwind.config.js new file mode 100644 index 0000000..c189a4a --- /dev/null +++ b/builder-frontend/tailwind.config.js @@ -0,0 +1,9 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: [], + theme: { + extend: {}, + }, + plugins: [], +} + diff --git a/builder-frontend/vite.config.js b/builder-frontend/vite.config.js new file mode 100644 index 0000000..4a303c3 --- /dev/null +++ b/builder-frontend/vite.config.js @@ -0,0 +1,6 @@ +import { defineConfig } from "vite"; +import solid from "vite-plugin-solid"; + +export default defineConfig({ + plugins: [solid()], +}); diff --git a/devbox.json b/devbox.json index e9cba17..188054a 100644 --- a/devbox.json +++ b/devbox.json @@ -3,7 +3,11 @@ "packages": [ "maven@latest", "quarkus@latest", - "jdk17@latest" + "jdk21@latest", + "google-cloud-sdk@latest", + "podman@latest", + "docker@latest", + "firebase-tools@latest" ], "shell": { "init_hook": [ diff --git a/devbox.lock b/devbox.lock index fd225f4..d643d9e 100644 --- a/devbox.lock +++ b/devbox.lock @@ -1,138 +1,351 @@ { "lockfile_version": "1", "packages": { + "docker@latest": { + "last_modified": "2025-08-05T11:35:34Z", + "resolved": "github:NixOS/nixpkgs/a683adc19ff5228af548c6539dbc3440509bfed3#docker", + "source": "devbox-search", + "version": "28.3.3", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/6x679ch4cqa5aywjsrnplsasv5rp0mak-docker-28.3.3", + "default": true + } + ], + "store_path": "/nix/store/6x679ch4cqa5aywjsrnplsasv5rp0mak-docker-28.3.3" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/wglkv9vb1q033d9kvvicrypmarpv0b5m-docker-28.3.3", + "default": true + } + ], + "store_path": "/nix/store/wglkv9vb1q033d9kvvicrypmarpv0b5m-docker-28.3.3" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/g3h7m6qsx5rkb908qcqi4a6kflvcriz5-docker-28.3.3", + "default": true + } + ], + "store_path": "/nix/store/g3h7m6qsx5rkb908qcqi4a6kflvcriz5-docker-28.3.3" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/7lr90z9f3m39fa98iy5g17l5cfraf733-docker-28.3.3", + "default": true + } + ], + "store_path": "/nix/store/7lr90z9f3m39fa98iy5g17l5cfraf733-docker-28.3.3" + } + } + }, + "firebase-tools@latest": { + "last_modified": "2025-07-28T17:09:23Z", + "resolved": "github:NixOS/nixpkgs/648f70160c03151bc2121d179291337ad6bc564b#firebase-tools", + "source": "devbox-search", + "version": "14.11.1", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/xi8dai4n5hq9n7v02csgc48d9x21j4z0-firebase-tools-14.11.1", + "default": true + } + ], + "store_path": "/nix/store/xi8dai4n5hq9n7v02csgc48d9x21j4z0-firebase-tools-14.11.1" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/vv94zvz0m96zzwn3mq4lncwlbkab7nxj-firebase-tools-14.11.1", + "default": true + } + ], + "store_path": "/nix/store/vv94zvz0m96zzwn3mq4lncwlbkab7nxj-firebase-tools-14.11.1" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/xprhm4dwy4bk5pz1az4dl3bbqy1wz4w8-firebase-tools-14.11.1", + "default": true + } + ], + "store_path": "/nix/store/xprhm4dwy4bk5pz1az4dl3bbqy1wz4w8-firebase-tools-14.11.1" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/76w96qfhwhwk6p1ln4mg5pvn5pl305kb-firebase-tools-14.11.1", + "default": true + } + ], + "store_path": "/nix/store/76w96qfhwhwk6p1ln4mg5pvn5pl305kb-firebase-tools-14.11.1" + } + } + }, "github:NixOS/nixpkgs/nixpkgs-unstable": { - "resolved": "github:NixOS/nixpkgs/3a05eebede89661660945da1f151959900903b6a?lastModified=1740547748&narHash=sha256-Ly2fBL1LscV%2BKyCqPRufUBuiw%2BzmWrlJzpWOWbahplg%3D" + "last_modified": "2025-08-05T11:35:34Z", + "resolved": "github:NixOS/nixpkgs/a683adc19ff5228af548c6539dbc3440509bfed3?lastModified=1754393734&narHash=sha256-fbnmAwTQkuXHKBlcL5Nq1sMAzd3GFqCOQgEQw6Hy0Ak%3D" + }, + "google-cloud-sdk@latest": { + "last_modified": "2025-07-28T17:09:23Z", + "resolved": "github:NixOS/nixpkgs/648f70160c03151bc2121d179291337ad6bc564b#google-cloud-sdk", + "source": "devbox-search", + "version": "529.0.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/9v8y10z0y9lkhjsaf728nhhbc4jbp83q-google-cloud-sdk-529.0.0", + "default": true + } + ], + "store_path": "/nix/store/9v8y10z0y9lkhjsaf728nhhbc4jbp83q-google-cloud-sdk-529.0.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/5kz4dghm4lc2rmjy85xz813irlpyglhl-google-cloud-sdk-529.0.0", + "default": true + } + ], + "store_path": "/nix/store/5kz4dghm4lc2rmjy85xz813irlpyglhl-google-cloud-sdk-529.0.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/arw4kq8hig5v2n3gq3pzw521qb3rc736-google-cloud-sdk-529.0.0", + "default": true + } + ], + "store_path": "/nix/store/arw4kq8hig5v2n3gq3pzw521qb3rc736-google-cloud-sdk-529.0.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/aw6lxd787x16w8d2ll2afnjjz3kqi0m4-google-cloud-sdk-529.0.0", + "default": true + } + ], + "store_path": "/nix/store/aw6lxd787x16w8d2ll2afnjjz3kqi0m4-google-cloud-sdk-529.0.0" + } + } }, - "jdk17@latest": { - "last_modified": "2025-01-19T08:16:51Z", - "resolved": "github:NixOS/nixpkgs/50165c4f7eb48ce82bd063e1fb8047a0f515f8ce#jdk17", + "jdk21@latest": { + "last_modified": "2024-06-12T20:55:33Z", + "resolved": "github:NixOS/nixpkgs/a9858885e197f984d92d7fe64e9fff6b2e488d40#jdk", "source": "devbox-search", - "version": "17.0.13+11", + "version": "21+35", "systems": { "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/791xnzdn9m059szs44mxx3gj99ks9wif-openjdk-17.0.13+11", + "path": "/nix/store/f7risq9mlz4gywxr65v46h4mv1a4a0s3-openjdk-21+35", "default": true }, { "name": "debug", - "path": "/nix/store/pcxw19bmmhival4gn0qgh3zcp6shc3cs-openjdk-17.0.13+11-debug" + "path": "/nix/store/lgljxsffciqlijp38iklrh1ki5vi0y4f-openjdk-21+35-debug" } ], - "store_path": "/nix/store/791xnzdn9m059szs44mxx3gj99ks9wif-openjdk-17.0.13+11" + "store_path": "/nix/store/f7risq9mlz4gywxr65v46h4mv1a4a0s3-openjdk-21+35" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/y2c3skvhv4l8h069ri1vkj0dwn7n6kcf-openjdk-17.0.13+11", + "path": "/nix/store/bk3x3ia7gxqic1jgr3dz05gwi8zw671y-openjdk-21+35", "default": true }, { "name": "debug", - "path": "/nix/store/zq94asm29gzzglah8lqr7h3zmzj050gd-openjdk-17.0.13+11-debug" + "path": "/nix/store/2h6zm6qy04amw9mvir8n5fifn99dzg16-openjdk-21+35-debug" } ], - "store_path": "/nix/store/y2c3skvhv4l8h069ri1vkj0dwn7n6kcf-openjdk-17.0.13+11" + "store_path": "/nix/store/bk3x3ia7gxqic1jgr3dz05gwi8zw671y-openjdk-21+35" } } }, "maven@latest": { - "last_modified": "2025-01-19T08:16:51Z", - "resolved": "github:NixOS/nixpkgs/50165c4f7eb48ce82bd063e1fb8047a0f515f8ce#maven", + "last_modified": "2025-07-28T17:09:23Z", + "resolved": "github:NixOS/nixpkgs/648f70160c03151bc2121d179291337ad6bc564b#maven", + "source": "devbox-search", + "version": "3.9.11", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/4l03gxm9lnkap9z4s219lk2lmznf6ksr-maven-3.9.11", + "default": true + } + ], + "store_path": "/nix/store/4l03gxm9lnkap9z4s219lk2lmznf6ksr-maven-3.9.11" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/6663sm7x2b6lh3lcy40ysng365h0n41w-maven-3.9.11", + "default": true + } + ], + "store_path": "/nix/store/6663sm7x2b6lh3lcy40ysng365h0n41w-maven-3.9.11" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/i1fz4dsqcfy2ilz9yq0z84n973iw4k8z-maven-3.9.11", + "default": true + } + ], + "store_path": "/nix/store/i1fz4dsqcfy2ilz9yq0z84n973iw4k8z-maven-3.9.11" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/f055dmaqf8japp7nqww78zp3g37j5qfs-maven-3.9.11", + "default": true + } + ], + "store_path": "/nix/store/f055dmaqf8japp7nqww78zp3g37j5qfs-maven-3.9.11" + } + } + }, + "podman@latest": { + "last_modified": "2025-08-04T20:54:38Z", + "resolved": "github:NixOS/nixpkgs/cab778239e705082fe97bb4990e0d24c50924c04#podman", "source": "devbox-search", - "version": "3.9.9", + "version": "5.5.2", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/10lcy7avwkxg2zc43ql1l0l2anrd73cw-maven-3.9.9", + "path": "/nix/store/d4qsjh71z5imdjn4fkixfq040i0l56bz-podman-5.5.2", + "default": true + }, + { + "name": "man", + "path": "/nix/store/lzqbp5rjw8zldnwy7zzzp6z5lffilsmh-podman-5.5.2-man", "default": true } ], - "store_path": "/nix/store/10lcy7avwkxg2zc43ql1l0l2anrd73cw-maven-3.9.9" + "store_path": "/nix/store/d4qsjh71z5imdjn4fkixfq040i0l56bz-podman-5.5.2" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/krm05c4zw3v3jqxzncx7q0r6l2q4x1j0-maven-3.9.9", + "path": "/nix/store/rq6rlyb9ihran9jqsadq6rgv5vc5ri1h-podman-5.5.2", + "default": true + }, + { + "name": "man", + "path": "/nix/store/b81md27ig417wpqmgpj6a6lzb3vcd0b6-podman-5.5.2-man", "default": true } ], - "store_path": "/nix/store/krm05c4zw3v3jqxzncx7q0r6l2q4x1j0-maven-3.9.9" + "store_path": "/nix/store/rq6rlyb9ihran9jqsadq6rgv5vc5ri1h-podman-5.5.2" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/frdcg7rf1ps9wvd31591n5i2gja4r14p-maven-3.9.9", + "path": "/nix/store/in7kybnm92wgpkz9vrdpwq6a4z2vzcqb-podman-5.5.2", + "default": true + }, + { + "name": "man", + "path": "/nix/store/gw2ia71qbhxnjf5xw2qa66kih03k0543-podman-5.5.2-man", "default": true } ], - "store_path": "/nix/store/frdcg7rf1ps9wvd31591n5i2gja4r14p-maven-3.9.9" + "store_path": "/nix/store/in7kybnm92wgpkz9vrdpwq6a4z2vzcqb-podman-5.5.2" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/zidqb732mgnhfw1iw5diqrf8ka4spm28-maven-3.9.9", + "path": "/nix/store/la6fdv93ng406rl4rkc372f0l7qbx51p-podman-5.5.2", + "default": true + }, + { + "name": "man", + "path": "/nix/store/3w7yy7xg9lzirnnk30dahcblfnxr4ldx-podman-5.5.2-man", "default": true } ], - "store_path": "/nix/store/zidqb732mgnhfw1iw5diqrf8ka4spm28-maven-3.9.9" + "store_path": "/nix/store/la6fdv93ng406rl4rkc372f0l7qbx51p-podman-5.5.2" } } }, "quarkus@latest": { - "last_modified": "2025-01-24T01:56:22Z", - "resolved": "github:NixOS/nixpkgs/4dfc61e9cd0c26b3bc3e6d6fac3ba10d18959fc4#quarkus", + "last_modified": "2025-07-28T17:09:23Z", + "resolved": "github:NixOS/nixpkgs/648f70160c03151bc2121d179291337ad6bc564b#quarkus", "source": "devbox-search", - "version": "3.17.7", + "version": "3.24.5", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/ldv64vkp9vld3inpxk487brzx3f3mnkh-quarkus-cli-3.17.7", + "path": "/nix/store/jjg2x9jzvqjs5nbjgg1gimghydgnmb2w-quarkus-cli-3.24.5", "default": true } ], - "store_path": "/nix/store/ldv64vkp9vld3inpxk487brzx3f3mnkh-quarkus-cli-3.17.7" + "store_path": "/nix/store/jjg2x9jzvqjs5nbjgg1gimghydgnmb2w-quarkus-cli-3.24.5" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/j559aann5k3ar315xwsw3vmamdzlpbq4-quarkus-cli-3.17.7", + "path": "/nix/store/729fg85hyhsf99lvii3w4g4bydbgx8w3-quarkus-cli-3.24.5", "default": true } ], - "store_path": "/nix/store/j559aann5k3ar315xwsw3vmamdzlpbq4-quarkus-cli-3.17.7" + "store_path": "/nix/store/729fg85hyhsf99lvii3w4g4bydbgx8w3-quarkus-cli-3.24.5" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/2isjms0pmg2byl8m3hx9xhsvr9p1lxcb-quarkus-cli-3.17.7", + "path": "/nix/store/qrinn9yr8h2z5nnlahwvxwb2l5szp704-quarkus-cli-3.24.5", "default": true } ], - "store_path": "/nix/store/2isjms0pmg2byl8m3hx9xhsvr9p1lxcb-quarkus-cli-3.17.7" + "store_path": "/nix/store/qrinn9yr8h2z5nnlahwvxwb2l5szp704-quarkus-cli-3.24.5" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/ys26s3n95a4yvayrsvj9nv48940z4fhy-quarkus-cli-3.17.7", + "path": "/nix/store/yr4hd7vjkn8kdq83c042cda4wka1izm4-quarkus-cli-3.24.5", "default": true } ], - "store_path": "/nix/store/ys26s3n95a4yvayrsvj9nv48940z4fhy-quarkus-cli-3.17.7" + "store_path": "/nix/store/yr4hd7vjkn8kdq83c042cda4wka1izm4-quarkus-cli-3.24.5" } } } diff --git a/.envrc b/dmn-library/.envrc similarity index 100% rename from .envrc rename to dmn-library/.envrc diff --git a/.gitattributes b/dmn-library/.gitattributes similarity index 100% rename from .gitattributes rename to dmn-library/.gitattributes diff --git a/dmn-library/.gitignore b/dmn-library/.gitignore new file mode 100644 index 0000000..e397f07 --- /dev/null +++ b/dmn-library/.gitignore @@ -0,0 +1,5 @@ +target/ + +thunder-tests/thunderResponses.json +thunder-tests/res-files/ +thunder-tests/thunderActivity.json diff --git a/.idx/default-vscode-settings.jsonc b/dmn-library/.idx/default-vscode-settings.jsonc similarity index 100% rename from .idx/default-vscode-settings.jsonc rename to dmn-library/.idx/default-vscode-settings.jsonc diff --git a/dmn-library/.idx/dev.nix b/dmn-library/.idx/dev.nix new file mode 100644 index 0000000..ecd9080 --- /dev/null +++ b/dmn-library/.idx/dev.nix @@ -0,0 +1,67 @@ +# To learn more about how to use Nix to configure your environment +# see: https://developers.google.com/idx/guides/customize-idx-env +{ pkgs, ... }: { + # Which nixpkgs channel to use. + channel = "stable-24.05"; # or "unstable" + + # Use https://search.nixos.org/packages to find packages + packages = [ + pkgs.maven + pkgs.quarkus + pkgs.jdk17 + ]; + + # Sets environment variables in the workspace + env = { + # PORT = "8080"; + }; + idx = { + # Search for the extensions you want on https://open-vsx.org/ and use "publisher.id" + extensions = [ + "jellydn.toggle-excluded-files" + "waderyan.gitblame" + "kie-group.dmn-vscode-extension" + "rangav.vscode-thunder-client" + "redhat.vscode-xml" + "mhutchie.git-graph" + "redhat.java" + "redhat.vscode-quarkus" + ]; + + # Enable previews + # previews = { + # enable = true; + # previews = { + # web = { + # command = [ + # "bin/dev" + # ]; + # manager = "web"; + # }; + # web = { + # # Example: run "npm run dev" with PORT set to IDX's defined port for previews, + # # and show it in IDX's web preview panel + # command = ["npm" "run" "dev"]; + # manager = "web"; + # env = { + # # Environment variables to set for your server + # PORT = "$PORT"; + # }; + # }; + # }; + # }; + + # Workspace lifecycle hooks + workspace = { + # Runs when a workspace is first created + onCreate = { + install-default-settings = "cp .idx/default-vscode-settings.jsonc /home/user/.codeoss-cloudworkstations/data/Machine/settings.json"; + install-thunder-client-tests = "ln -s /home/user/dmn-benefit-toolbox/thunder-tests/ /home/user/.codeoss-cloudworkstations/data/User/globalStorage/rangav.vscode-thunder-client"; + }; + # Runs when the workspace is (re)started + onStart = { + start-quarkus-dev-server = "bin/dev"; + }; + }; + }; +} diff --git a/dmn-library/.idx/integrations.json b/dmn-library/.idx/integrations.json new file mode 100644 index 0000000..19d81fc --- /dev/null +++ b/dmn-library/.idx/integrations.json @@ -0,0 +1,7 @@ +{ + "cloud_run_deploy": { + "region": "us-east4", + "sourceFlag": "--source .", + "allowUnauthenticatedInvocationsFlag": "--allow-unauthenticated" + } +} \ No newline at end of file diff --git a/.vscode/DMN Benefit Toolbox.code-profile b/dmn-library/.vscode/DMN Benefit Toolbox.code-profile similarity index 100% rename from .vscode/DMN Benefit Toolbox.code-profile rename to dmn-library/.vscode/DMN Benefit Toolbox.code-profile diff --git a/.vscode/extensions.json b/dmn-library/.vscode/extensions.json similarity index 100% rename from .vscode/extensions.json rename to dmn-library/.vscode/extensions.json diff --git a/.vscode/launch.json b/dmn-library/.vscode/launch.json similarity index 100% rename from .vscode/launch.json rename to dmn-library/.vscode/launch.json diff --git a/.vscode/settings.json b/dmn-library/.vscode/settings.json similarity index 100% rename from .vscode/settings.json rename to dmn-library/.vscode/settings.json diff --git a/.vscode/tasks.json b/dmn-library/.vscode/tasks.json similarity index 100% rename from .vscode/tasks.json rename to dmn-library/.vscode/tasks.json diff --git a/Dockerfile b/dmn-library/Dockerfile similarity index 100% rename from Dockerfile rename to dmn-library/Dockerfile diff --git a/LICENSE.md b/dmn-library/LICENSE.md similarity index 100% rename from LICENSE.md rename to dmn-library/LICENSE.md diff --git a/dmn-library/README.md b/dmn-library/README.md new file mode 100644 index 0000000..f5e9f62 --- /dev/null +++ b/dmn-library/README.md @@ -0,0 +1,119 @@ +# Benefit Decision Toolkit + +**Use [DMN](https://www.omg.org/dmn/) and [FEEL](https://docs.camunda.io/docs/components/modeler/feel/what-is-feel/) to create APIs and Screeners for public benefit rules.** + +## Motivation + +Why hire a team of software engineers to codify rules that your benefit experts already know inside and out? + +Why design a screening tool from scratch when your goal is simply to deploy a functionally accurate service as quickly as possible? + +***Benefit Decision Toolkit simplifies the management of eligibility rules and screeners so motivated subject matter experts can create useful tools with less hand-holding from traditional software teams.*** + +## Examples + +### The Philly Property Tax Relief Screener + +As a proof of concept, we've built a screener for several of the [tax relief benefits available in Philadelphia](https://www.phila.gov/services/payments-assistance-taxes/taxes/property-and-real-estate-taxes/get-real-estate-tax-relief/). + +You can interact with the screener yourself at: https://phillypropertytaxrelief.org. + +### JSON ("REST") API + +A JSON API is generated from the eligibility rules created in DMN/FEEL. You can interact with the endpoints of this API at: https://phillypropertytaxrelief.org/q/swagger-ui/ + +## Setup a Development Environment + +Since the ultimate goal of this project is to enable a development environment that is accessible to those without a software engineering background, we are intrigued by tools like [Firebase Studio](https://firebase.google.com/docs/studio) as a precursor/mockup for a more specialized service that we'd like to build (eventually). + +With Firebase Studio, the environment is in the cloud, and can be accessed from any computer without any manual setup. + +If you're an experienced engineer, however, you may want to skip Firebase Studio in favor of your own preferred setup. (Up to you.) + +### Use Firebase Studio (simplest) + +The easiest way to get a feel for Benefit Decision Toolkit is to [fork this repo*](https://github.com/CodeForPhilly/benefit-decision-toolkit/fork) and then open it (from the fork!) in Firebase Studio: + + + + + + Open in Firebase Studio + + + +When you open Benefit Decision Toolkit in Firebase Studio, a development machine will be created and configured for you in the cloud. (This will take a few minutes.) + +After the first time you open the project, you can just return to https://studio.firebase.google.com to pick up where you left off. + +_*Note on forking: you can skip this step if you are just curious or otherwise don't intend to contribute changes back to the project._ + +### Use Devbox (medium simple) + +The second easiest way to get started is to [fork this repo*](https://github.com/CodeForPhilly/benefit-decision-toolkit/fork), clone it to your local machine, and use the included Devbox configuration: + +[![Built with Devbox](https://www.jetify.com/img/devbox/shield_galaxy.svg)](https://www.jetify.com/devbox/docs/contributor-quickstart/) + +[Devbox](https://www.jetify.com/devbox) is a way to create isolated development environments on your own machine. + +We're experimenting w/ Devbox for the same reason we're experimenting w/ Firebase Studio; we're interested in how this approach to managing development environments could form the basis of some future specialized/packaged toolkit. + +### Roll your own (for experienced developers) + +Of course, you can also manually setup a laptop/desktop to work on Benefit Decision Toolkit, but it isn't recommended unless you really know what you're doing. + +### Additional setup notes + +#### Forms development + +If you're planning to develop forms (the basis of eligibility screeners) in addition to eligibility rules, then you'll also need to download [Camunda Modeler](https://camunda.com/download/modeler/). + +#### Kicking the tires and testing your work + +You can run `bin/dev`* from a terminal to start the Quarkus development server. This will serve the API and any existing eligibility screeners from `https://localhost:8080`**. + +*_If you are using Firebase Studio the Quarkus dev server should automatically start when you load the workspace._ + +**_Since Firebase Studio is a browser-based environment, the system will map the URL for the dev server to an address you can reach from a browser in your local machine. You can access this address by clicking on the Firebase Studio logo in the left sidebar, then clicking into the "Backend Ports" panel._ + +Screenshot of Backend Ports list in Firebase Studio + +## Technologies overview + +We use a combination of open-source tools ([Kogito](https://kogito.kie.org/) and [form-js](https://bpmn.io/toolkit/form-js/)) with some scaffolding to tie them together and make them easier to use. + +Here are some high-level things to orient you... + +### DMN Files + +DMN files (`.dmn`) can be edited directly in VS Code via the [DMN Editor extension](https://marketplace.visualstudio.com/items?itemName=kie-group.dmn-vscode-extension). If you're using Firebase Studio as described above, then this extension is already installed for you. If you're using another dev environment, then you'll need to install the extension manually. + +You may occasionally need to interact with the raw XML text of the DMN; this can be done via the "Reopen with Text Editor" feature of VS Code. + +A good orientation on the basics of DMN can be found [here](https://learn-dmn-in-15-minutes.com/). + +In this project, DMN (and its accompanying expression language FEEL) acts as the "source code" for a JSON web API. Kogito generates this API (with Java) when you run the Quarkus development server (automatic with Firebase Studio, or by running the `bin/dev` script). + +### Form Files + +Form files (`.form`) can be edited using [Camunda Modeler](https://camunda.com/download/modeler/). The modeler provides a UI for designing the form's layout and logic (which often incorporates FEEL expressions). + +Behind the scenes,the form is saved in JSON format. + +When it comes time to use the form in a screener, form-js interprets this JSON and uses it to display the form on a web page. + +### Eligibility Screeners + +To create the [Philadelphia Property Tax Relief Screener](https://phillypropertytaxrelief.org), we've written a [Qute template](https://quarkus.io/guides/qute) which displays the form, posts data as it is collected to the eligibility API (the one that is built from the DMN), and receives back eligibility results for display on the form. + +For future screeners, we envision packaging up this functionality somehow, allowing the entire screening and results process to be included as part of other websites and tools, the content of which is outside the scope of this project. + +## License +See the [LICENSE](https://github.com/CodeForPhilly/benefit-decision-toolkit/blob/main/LICENSE.md) file for license rights and limitations (MIT). diff --git a/bin/deploy-default b/dmn-library/bin/deploy-default similarity index 100% rename from bin/deploy-default rename to dmn-library/bin/deploy-default diff --git a/bin/dev b/dmn-library/bin/dev similarity index 100% rename from bin/dev rename to dmn-library/bin/dev diff --git a/docs/images/backend-ports.png b/dmn-library/docs/images/backend-ports.png similarity index 100% rename from docs/images/backend-ports.png rename to dmn-library/docs/images/backend-ports.png diff --git a/pom.xml b/dmn-library/pom.xml similarity index 100% rename from pom.xml rename to dmn-library/pom.xml diff --git a/src/main/java/org/prestoncabe/frontend/PaceScreenerResource.java b/dmn-library/src/main/java/org/prestoncabe/frontend/PaceScreenerResource.java similarity index 100% rename from src/main/java/org/prestoncabe/frontend/PaceScreenerResource.java rename to dmn-library/src/main/java/org/prestoncabe/frontend/PaceScreenerResource.java diff --git a/src/main/java/org/prestoncabe/frontend/PhlPropertyTaxReliefResource.java b/dmn-library/src/main/java/org/prestoncabe/frontend/PhlPropertyTaxReliefResource.java similarity index 100% rename from src/main/java/org/prestoncabe/frontend/PhlPropertyTaxReliefResource.java rename to dmn-library/src/main/java/org/prestoncabe/frontend/PhlPropertyTaxReliefResource.java diff --git a/src/main/java/org/prestoncabe/frontend/ViewerResource.java b/dmn-library/src/main/java/org/prestoncabe/frontend/ViewerResource.java similarity index 100% rename from src/main/java/org/prestoncabe/frontend/ViewerResource.java rename to dmn-library/src/main/java/org/prestoncabe/frontend/ViewerResource.java diff --git a/src/main/java/org/prestoncabe/functions/LocationService.java b/dmn-library/src/main/java/org/prestoncabe/functions/LocationService.java similarity index 100% rename from src/main/java/org/prestoncabe/functions/LocationService.java rename to dmn-library/src/main/java/org/prestoncabe/functions/LocationService.java diff --git a/src/main/resources/META-INF/resources/forms/paceScreener.form b/dmn-library/src/main/resources/META-INF/resources/forms/paceScreener.form similarity index 100% rename from src/main/resources/META-INF/resources/forms/paceScreener.form rename to dmn-library/src/main/resources/META-INF/resources/forms/paceScreener.form diff --git a/src/main/resources/META-INF/resources/forms/phlPreK.form b/dmn-library/src/main/resources/META-INF/resources/forms/phlPreK.form similarity index 100% rename from src/main/resources/META-INF/resources/forms/phlPreK.form rename to dmn-library/src/main/resources/META-INF/resources/forms/phlPreK.form diff --git a/src/main/resources/META-INF/resources/forms/phlPropertyTaxRelief.form b/dmn-library/src/main/resources/META-INF/resources/forms/phlPropertyTaxRelief.form similarity index 100% rename from src/main/resources/META-INF/resources/forms/phlPropertyTaxRelief.form rename to dmn-library/src/main/resources/META-INF/resources/forms/phlPropertyTaxRelief.form diff --git a/src/main/resources/META-INF/resources/images/favicon.png b/dmn-library/src/main/resources/META-INF/resources/images/favicon.png similarity index 100% rename from src/main/resources/META-INF/resources/images/favicon.png rename to dmn-library/src/main/resources/META-INF/resources/images/favicon.png diff --git a/src/main/resources/META-INF/resources/images/philly-skyline.jpg b/dmn-library/src/main/resources/META-INF/resources/images/philly-skyline.jpg similarity index 100% rename from src/main/resources/META-INF/resources/images/philly-skyline.jpg rename to dmn-library/src/main/resources/META-INF/resources/images/philly-skyline.jpg diff --git a/src/main/resources/META-INF/resources/stylesheets/screenerStyle.css b/dmn-library/src/main/resources/META-INF/resources/stylesheets/screenerStyle.css similarity index 100% rename from src/main/resources/META-INF/resources/stylesheets/screenerStyle.css rename to dmn-library/src/main/resources/META-INF/resources/stylesheets/screenerStyle.css diff --git a/src/main/resources/age.dmn b/dmn-library/src/main/resources/age.dmn similarity index 100% rename from src/main/resources/age.dmn rename to dmn-library/src/main/resources/age.dmn diff --git a/src/main/resources/application.properties b/dmn-library/src/main/resources/application.properties similarity index 100% rename from src/main/resources/application.properties rename to dmn-library/src/main/resources/application.properties diff --git a/src/main/resources/benefits.dmn b/dmn-library/src/main/resources/benefits.dmn similarity index 100% rename from src/main/resources/benefits.dmn rename to dmn-library/src/main/resources/benefits.dmn diff --git a/dmn-library/src/main/resources/data/locations.db b/dmn-library/src/main/resources/data/locations.db new file mode 100644 index 0000000..ccfb0bd Binary files /dev/null and b/dmn-library/src/main/resources/data/locations.db differ diff --git a/src/main/resources/enrollments.dmn b/dmn-library/src/main/resources/enrollments.dmn similarity index 100% rename from src/main/resources/enrollments.dmn rename to dmn-library/src/main/resources/enrollments.dmn diff --git a/src/main/resources/housing.dmn b/dmn-library/src/main/resources/housing.dmn similarity index 100% rename from src/main/resources/housing.dmn rename to dmn-library/src/main/resources/housing.dmn diff --git a/src/main/resources/income.dmn b/dmn-library/src/main/resources/income.dmn similarity index 100% rename from src/main/resources/income.dmn rename to dmn-library/src/main/resources/income.dmn diff --git a/src/main/resources/location.dmn b/dmn-library/src/main/resources/location.dmn similarity index 100% rename from src/main/resources/location.dmn rename to dmn-library/src/main/resources/location.dmn diff --git a/src/main/resources/templates/PaceScreener.qute.html b/dmn-library/src/main/resources/templates/PaceScreener.qute.html similarity index 100% rename from src/main/resources/templates/PaceScreener.qute.html rename to dmn-library/src/main/resources/templates/PaceScreener.qute.html diff --git a/src/main/resources/templates/phlPropertyTaxRelief.qute.html b/dmn-library/src/main/resources/templates/phlPropertyTaxRelief.qute.html similarity index 100% rename from src/main/resources/templates/phlPropertyTaxRelief.qute.html rename to dmn-library/src/main/resources/templates/phlPropertyTaxRelief.qute.html diff --git a/src/main/resources/templates/viewer.qute.html b/dmn-library/src/main/resources/templates/viewer.qute.html similarity index 100% rename from src/main/resources/templates/viewer.qute.html rename to dmn-library/src/main/resources/templates/viewer.qute.html diff --git a/src/main/resources/utility.dmn b/dmn-library/src/main/resources/utility.dmn similarity index 100% rename from src/main/resources/utility.dmn rename to dmn-library/src/main/resources/utility.dmn diff --git a/thunder-tests/collections/all-tests/_info.json b/dmn-library/thunder-tests/collections/all-tests/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/_info.json rename to dmn-library/thunder-tests/collections/all-tests/_info.json diff --git a/thunder-tests/collections/all-tests/age-dmn/_info.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/_info.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/_info.json diff --git a/thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/60-years-old.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/60-years-old.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/60-years-old.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/60-years-old.json diff --git a/thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/_info.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/_info.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/_info.json diff --git a/thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/future-age.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/future-age.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/future-age.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/future-age.json diff --git a/thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/less-than-a-year-old.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/less-than-a-year-old.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/less-than-a-year-old.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/less-than-a-year-old.json diff --git a/thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/null-comparison-date.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/null-comparison-date.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/null-comparison-date.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/null-comparison-date.json diff --git a/thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/null-date-of-birth.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/null-date-of-birth.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/null-date-of-birth.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-date-bkm/null-date-of-birth.json diff --git a/thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/99-years-old.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/99-years-old.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/99-years-old.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/99-years-old.json diff --git a/thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/_info.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/_info.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/_info.json diff --git a/thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/exactly-1-year-old.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/exactly-1-year-old.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/exactly-1-year-old.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/exactly-1-year-old.json diff --git a/thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/less-than-a-year-old.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/less-than-a-year-old.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/less-than-a-year-old.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/less-than-a-year-old.json diff --git a/thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/null-date-of-birth.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/null-date-of-birth.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/null-date-of-birth.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/as-of-today-bkm/null-date-of-birth.json diff --git a/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/2-years-old.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/2-years-old.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/2-years-old.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/2-years-old.json diff --git a/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/3-years-old.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/3-years-old.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/3-years-old.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/3-years-old.json diff --git a/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/4-years-old.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/4-years-old.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/4-years-old.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/4-years-old.json diff --git a/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/5-years-old.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/5-years-old.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/5-years-old.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/5-years-old.json diff --git a/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/_info.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/_info.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/_info.json diff --git a/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/empty-post-data.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/empty-post-data.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/empty-post-data.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/empty-post-data.json diff --git a/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/inputs-are-null.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/inputs-are-null.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/inputs-are-null.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/inputs-are-null.json diff --git a/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/no-people.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/no-people.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/no-people.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/no-people.json diff --git a/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/people-without-date-ofbirth.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/people-without-date-ofbirth.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/people-without-date-ofbirth.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/child-age-three-orfour-sept-first/people-without-date-ofbirth.json diff --git a/thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/43-years-old.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/43-years-old.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/43-years-old.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/43-years-old.json diff --git a/thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/_info.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/_info.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/_info.json diff --git a/thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/born-this-year.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/born-this-year.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/born-this-year.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/born-this-year.json diff --git a/thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/less-than-a-year-old.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/less-than-a-year-old.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/less-than-a-year-old.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/less-than-a-year-old.json diff --git a/thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/null-date-of-birth.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/null-date-of-birth.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/null-date-of-birth.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/end-of-last-year-bkm/null-date-of-birth.json diff --git a/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/_info.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/_info.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/_info.json diff --git a/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/age-49-dob-at-top-level.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/age-49-dob-at-top-level.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/age-49-dob-at-top-level.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/age-49-dob-at-top-level.json diff --git a/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/age-50-dob-at-top-level.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/age-50-dob-at-top-level.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/age-50-dob-at-top-level.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/age-50-dob-at-top-level.json diff --git a/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/age-64-dob-inside-people.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/age-64-dob-inside-people.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/age-64-dob-inside-people.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/age-64-dob-inside-people.json diff --git a/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/age-65-dob-inside-people.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/age-65-dob-inside-people.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/age-65-dob-inside-people.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/age-65-dob-inside-people.json diff --git a/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/empty.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/empty.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/empty.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/empty.json diff --git a/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/people-with-null-dob-or-not-primary.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/people-with-null-dob-or-not-primary.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/people-with-null-dob-or-not-primary.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-fifty-in2025/people-with-null-dob-or-not-primary.json diff --git a/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/_info.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/_info.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/_info.json diff --git a/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/age-64-dob-at-top-level-2.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/age-64-dob-at-top-level-2.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/age-64-dob-at-top-level-2.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/age-64-dob-at-top-level-2.json diff --git a/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/age-64-dob-inside-people.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/age-64-dob-inside-people.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/age-64-dob-inside-people.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/age-64-dob-inside-people.json diff --git a/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/age-65-dob-at-top-level.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/age-65-dob-at-top-level.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/age-65-dob-at-top-level.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/age-65-dob-at-top-level.json diff --git a/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/age-65-dob-inside-people.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/age-65-dob-inside-people.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/age-65-dob-inside-people.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/age-65-dob-inside-people.json diff --git a/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/empty.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/empty.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/empty.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/empty.json diff --git a/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/people-with-null-dob-or-not-primary.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/people-with-null-dob-or-not-primary.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/people-with-null-dob-or-not-primary.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-person-atleast-sixty-five-i/people-with-null-dob-or-not-primary.json diff --git a/thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/_info.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/_info.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/_info.json diff --git a/thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/age-64.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/age-64.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/age-64.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/age-64.json diff --git a/thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/age-65.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/age-65.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/age-65.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/age-65.json diff --git a/thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/empty.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/empty.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/empty.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/empty.json diff --git a/thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/people-with-null-dob-or-not-spouse.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/people-with-null-dob-or-not-spouse.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/people-with-null-dob-or-not-spouse.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/primary-spouse-atleast-sixty-five-i/people-with-null-dob-or-not-spouse.json diff --git a/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/100-years-old.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/100-years-old.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/100-years-old.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/100-years-old.json diff --git a/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/59-years-old.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/59-years-old.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/59-years-old.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/59-years-old.json diff --git a/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/60-years-old.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/60-years-old.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/60-years-old.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/60-years-old.json diff --git a/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/_info.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/_info.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/_info.json diff --git a/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/blank.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/blank.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/blank.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/blank.json diff --git a/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/dob-blank-and-non-blank-is-over-60.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/dob-blank-and-non-blank-is-over-60.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/dob-blank-and-non-blank-is-over-60.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/dob-blank-and-non-blank-is-over-60.json diff --git a/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/dob-blank-and-non-blank-is-under-60.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/dob-blank-and-non-blank-is-under-60.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/dob-blank-and-non-blank-is-under-60.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/dob-blank-and-non-blank-is-under-60.json diff --git a/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/dob-blank.json b/dmn-library/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/dob-blank.json similarity index 100% rename from thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/dob-blank.json rename to dmn-library/thunder-tests/collections/all-tests/age-dmn/someone-age-sixty-orolder/dob-blank.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/_info.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/_info.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/_info.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/age-ineligible-pace-and-sfb.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/age-ineligible-pace-and-sfb.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/age-ineligible-pace-and-sfb.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/age-ineligible-pace-and-sfb.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/benefit-hh-enrolled-in-both-pace-ne.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/benefit-hh-enrolled-in-both-pace-ne.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/benefit-hh-enrolled-in-both-pace-ne.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/benefit-hh-enrolled-in-both-pace-ne.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/blank.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/blank.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/blank.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/blank.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/does-not-live-in-pa-given.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/does-not-live-in-pa-given.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/does-not-live-in-pa-given.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/does-not-live-in-pa-given.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/enrolled-in-medicaid-rx-but-not-pac.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/enrolled-in-medicaid-rx-but-not-pac.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/enrolled-in-medicaid-rx-but-not-pac.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/enrolled-in-medicaid-rx-but-not-pac.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/enrolled-in-pace-net-but-not-medica.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/enrolled-in-pace-net-but-not-medica.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/enrolled-in-pace-net-but-not-medica.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/enrolled-in-pace-net-but-not-medica.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/lives-in-pa-given.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/lives-in-pa-given.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/lives-in-pa-given.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/lives-in-pa-given.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-at-pace-limit.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-at-pace-limit.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-at-pace-limit.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-at-pace-limit.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-both-65age-eligible.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-both-65age-eligible.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-both-65age-eligible.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-both-65age-eligible.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-both-under-65age-ineligible.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-both-under-65age-ineligible.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-both-under-65age-ineligible.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-both-under-65age-ineligible.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-only-primary-65age-eligible.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-only-primary-65age-eligible.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-only-primary-65age-eligible.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-only-primary-65age-eligible.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-only-spouse-65age-eligible.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-only-spouse-65age-eligible.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-only-spouse-65age-eligible.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-only-spouse-65age-eligible.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-over-pace-limit.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-over-pace-limit.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-over-pace-limit.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/married-over-pace-limit.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/not-enrolled-in-pace-or-pnet-or-med.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/not-enrolled-in-pace-or-pnet-or-med.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/not-enrolled-in-pace-or-pnet-or-med.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/not-enrolled-in-pace-or-pnet-or-med.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/pace-age-ineligible-but-sfb-eligibl.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/pace-age-ineligible-but-sfb-eligibl.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/pace-age-ineligible-but-sfb-eligibl.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/pace-age-ineligible-but-sfb-eligibl.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/pace-and-sfb-eligible.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/pace-and-sfb-eligible.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/pace-and-sfb-eligible.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/pace-and-sfb-eligible.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/single-64-yrs-oldage-ineligible.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/single-64-yrs-oldage-ineligible.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/single-64-yrs-oldage-ineligible.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/single-64-yrs-oldage-ineligible.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/single-65-yrs-oldage-eligible.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/single-65-yrs-oldage-eligible.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/single-65-yrs-oldage-eligible.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/single-65-yrs-oldage-eligible.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/single-at-pacenet-limit.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/single-at-pacenet-limit.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/single-at-pacenet-limit.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/single-at-pacenet-limit.json diff --git a/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/single-over-pace-limit.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/single-over-pace-limit.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/single-over-pace-limit.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-pace-and-pnet/single-over-pace-limit.json diff --git a/thunder-tests/collections/all-tests/benefitspa-senior-food-box/11-person-age-and-income-eligible-h.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/11-person-age-and-income-eligible-h.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-senior-food-box/11-person-age-and-income-eligible-h.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/11-person-age-and-income-eligible-h.json diff --git a/thunder-tests/collections/all-tests/benefitspa-senior-food-box/11-person-ineligible-income.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/11-person-ineligible-income.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-senior-food-box/11-person-ineligible-income.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/11-person-ineligible-income.json diff --git a/thunder-tests/collections/all-tests/benefitspa-senior-food-box/2-person-age-and-income-eligible-but-already-enrol-2.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/2-person-age-and-income-eligible-but-already-enrol-2.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-senior-food-box/2-person-age-and-income-eligible-but-already-enrol-2.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/2-person-age-and-income-eligible-but-already-enrol-2.json diff --git a/thunder-tests/collections/all-tests/benefitspa-senior-food-box/2-person-age-and-income-eligible.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/2-person-age-and-income-eligible.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-senior-food-box/2-person-age-and-income-eligible.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/2-person-age-and-income-eligible.json diff --git a/thunder-tests/collections/all-tests/benefitspa-senior-food-box/2-person-age-eligible-household-but-income-ineligi-2.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/2-person-age-eligible-household-but-income-ineligi-2.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-senior-food-box/2-person-age-eligible-household-but-income-ineligi-2.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/2-person-age-eligible-household-but-income-ineligi-2.json diff --git a/thunder-tests/collections/all-tests/benefitspa-senior-food-box/3-person-income-eligible-income-typ-2.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/3-person-income-eligible-income-typ-2.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-senior-food-box/3-person-income-eligible-income-typ-2.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/3-person-income-eligible-income-typ-2.json diff --git a/thunder-tests/collections/all-tests/benefitspa-senior-food-box/3-person-income-inel-income-types-p.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/3-person-income-inel-income-types-p.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-senior-food-box/3-person-income-inel-income-types-p.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/3-person-income-inel-income-types-p.json diff --git a/thunder-tests/collections/all-tests/benefitspa-senior-food-box/5-person-income-eligible-total-inco.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/5-person-income-eligible-total-inco.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-senior-food-box/5-person-income-eligible-total-inco.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/5-person-income-eligible-total-inco.json diff --git a/thunder-tests/collections/all-tests/benefitspa-senior-food-box/5-person-income-in-eligible-total-i.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/5-person-income-in-eligible-total-i.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-senior-food-box/5-person-income-in-eligible-total-i.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/5-person-income-in-eligible-total-i.json diff --git a/thunder-tests/collections/all-tests/benefitspa-senior-food-box/_info.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-senior-food-box/_info.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/_info.json diff --git a/thunder-tests/collections/all-tests/benefitspa-senior-food-box/age-ineligible.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/age-ineligible.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-senior-food-box/age-ineligible.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/age-ineligible.json diff --git a/thunder-tests/collections/all-tests/benefitspa-senior-food-box/blank.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/blank.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-senior-food-box/blank.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/blank.json diff --git a/thunder-tests/collections/all-tests/benefitspa-senior-food-box/does-not-live-in-pa.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/does-not-live-in-pa.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-senior-food-box/does-not-live-in-pa.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/does-not-live-in-pa.json diff --git a/thunder-tests/collections/all-tests/benefitspa-senior-food-box/lives-in-pa-calculated-from-county-fips.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/lives-in-pa-calculated-from-county-fips.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-senior-food-box/lives-in-pa-calculated-from-county-fips.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/lives-in-pa-calculated-from-county-fips.json diff --git a/thunder-tests/collections/all-tests/benefitspa-senior-food-box/lives-in-pa-calculated-from-state-abbrev.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/lives-in-pa-calculated-from-state-abbrev.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-senior-food-box/lives-in-pa-calculated-from-state-abbrev.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/lives-in-pa-calculated-from-state-abbrev.json diff --git a/thunder-tests/collections/all-tests/benefitspa-senior-food-box/lives-in-pa-given.json b/dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/lives-in-pa-given.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitspa-senior-food-box/lives-in-pa-given.json rename to dmn-library/thunder-tests/collections/all-tests/benefitspa-senior-food-box/lives-in-pa-given.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-homestead/_info.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-homestead/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-homestead/_info.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-homestead/_info.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-homestead/already-enrolled-in-homestead.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-homestead/already-enrolled-in-homestead.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-homestead/already-enrolled-in-homestead.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-homestead/already-enrolled-in-homestead.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-homestead/eligible-for-homestead.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-homestead/eligible-for-homestead.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-homestead/eligible-for-homestead.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-homestead/eligible-for-homestead.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-homestead/eligible-with-equitable-interest-in-prop.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-homestead/eligible-with-equitable-interest-in-prop.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-homestead/eligible-with-equitable-interest-in-prop.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-homestead/eligible-with-equitable-interest-in-prop.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-homestead/inel-has-tax-abatement.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-homestead/inel-has-tax-abatement.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-homestead/inel-has-tax-abatement.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-homestead/inel-has-tax-abatement.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-homestead/inel-not-owner-occupant.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-homestead/inel-not-owner-occupant.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-homestead/inel-not-owner-occupant.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-homestead/inel-not-owner-occupant.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-homestead/ineligible-for-homestead-not-a-homeowner.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-homestead/ineligible-for-homestead-not-a-homeowner.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-homestead/ineligible-for-homestead-not-a-homeowner.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-homestead/ineligible-for-homestead-not-a-homeowner.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-homestead/not-in-philly.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-homestead/not-in-philly.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-homestead/not-in-philly.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-homestead/not-in-philly.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/1-hhtotal-hh-incomeno-current-year.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/1-hhtotal-hh-incomeno-current-year.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/1-hhtotal-hh-incomeno-current-year.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/1-hhtotal-hh-incomeno-current-year.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/2-hh-eligibletotal-prev-yr-income-p.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/2-hh-eligibletotal-prev-yr-income-p.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/2-hh-eligibletotal-prev-yr-income-p.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/2-hh-eligibletotal-prev-yr-income-p.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/2-hh-ineligibletotal-prev-yr-income-2.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/2-hh-ineligibletotal-prev-yr-income-2.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/2-hh-ineligibletotal-prev-yr-income-2.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/2-hh-ineligibletotal-prev-yr-income-2.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/3-hh-eligibilecurrent-mo-income-per.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/3-hh-eligibilecurrent-mo-income-per.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/3-hh-eligibilecurrent-mo-income-per.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/3-hh-eligibilecurrent-mo-income-per.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/3-hh-ineligibilecurrent-mo-income-p.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/3-hh-ineligibilecurrent-mo-income-p.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/3-hh-ineligibilecurrent-mo-income-p.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/3-hh-ineligibilecurrent-mo-income-p.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/4-hh-eligibletotal-hh-incomeat-limi.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/4-hh-eligibletotal-hh-incomeat-limi.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/4-hh-eligibletotal-hh-incomeat-limi.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/4-hh-eligibletotal-hh-incomeat-limi.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/4-hh-ineligibletotal-hh-incomeover.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/4-hh-ineligibletotal-hh-incomeover.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/4-hh-ineligibletotal-hh-incomeover.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/4-hh-ineligibletotal-hh-incomeover.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/_info.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/_info.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/_info.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/blank.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/blank.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/blank.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/blank.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/eligiblenot-tax-delinquent.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/eligiblenot-tax-delinquent.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/eligiblenot-tax-delinquent.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/eligiblenot-tax-delinquent.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/eligibletax-delinquent-but-enrolled.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/eligibletax-delinquent-but-enrolled.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/eligibletax-delinquent-but-enrolled.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/eligibletax-delinquent-but-enrolled.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblealready-enrolled-in-phl-l.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblealready-enrolled-in-phl-l.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblealready-enrolled-in-phl-l.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblealready-enrolled-in-phl-l.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblehas-10-year-tax-abatement.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblehas-10-year-tax-abatement.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblehas-10-year-tax-abatement.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblehas-10-year-tax-abatement.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-10-year-owner-occupan.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-10-year-owner-occupan.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-10-year-owner-occupan.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-10-year-owner-occupan.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-a-homeowner-or-equita.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-a-homeowner-or-equita.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-a-homeowner-or-equita.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-a-homeowner-or-equita.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-in-philly.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-in-philly.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-in-philly.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-in-philly.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-loop-tax-assessment-e.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-loop-tax-assessment-e.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-loop-tax-assessment-e.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-loop-tax-assessment-e.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-owner-occupant.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-owner-occupant.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-owner-occupant.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/ineligiblenot-owner-occupant.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/ineligibletax-delinquent-and-not-on.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/ineligibletax-delinquent-and-not-on.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/ineligibletax-delinquent-and-not-on.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/ineligibletax-delinquent-and-not-on.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-loop/might-be-eligiblein-philly-via-zip.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/might-be-eligiblein-philly-via-zip.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-loop/might-be-eligiblein-philly-via-zip.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-loop/might-be-eligiblein-philly-via-zip.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-oopa/_info.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-oopa/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-oopa/_info.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-oopa/_info.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-oopa/eligiblemeets-all-criteria.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-oopa/eligiblemeets-all-criteria.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-oopa/eligiblemeets-all-criteria.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-oopa/eligiblemeets-all-criteria.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-oopa/eligiblenot-homeowner-but-has-equit.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-oopa/eligiblenot-homeowner-but-has-equit.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-oopa/eligiblenot-homeowner-but-has-equit.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-oopa/eligiblenot-homeowner-but-has-equit.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblealready-enrolled-in-oopa.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblealready-enrolled-in-oopa.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblealready-enrolled-in-oopa.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblealready-enrolled-in-oopa.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblenot-homeowner.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblenot-homeowner.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblenot-homeowner.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblenot-homeowner.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblenot-in-philly.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblenot-in-philly.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblenot-in-philly.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblenot-in-philly.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblenot-owner-occupant.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblenot-owner-occupant.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblenot-owner-occupant.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblenot-owner-occupant.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblenot-tax-delinquent.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblenot-tax-delinquent.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblenot-tax-delinquent.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-oopa/ineligiblenot-tax-delinquent.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-prek/_info.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-prek/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-prek/_info.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-prek/_info.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-prek/age-eligible-but-already-enrolled.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-prek/age-eligible-but-already-enrolled.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-prek/age-eligible-but-already-enrolled.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-prek/age-eligible-but-already-enrolled.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-prek/age-ineligible-for-phl-prek.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-prek/age-ineligible-for-phl-prek.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-prek/age-ineligible-for-phl-prek.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-prek/age-ineligible-for-phl-prek.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-prek/in-philly-via-zip-code.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-prek/in-philly-via-zip-code.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-prek/in-philly-via-zip-code.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-prek/in-philly-via-zip-code.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-prek/not-in-philly-2.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-prek/not-in-philly-2.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-prek/not-in-philly-2.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-prek/not-in-philly-2.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-prek/phl-prek-eligible-2.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-prek/phl-prek-eligible-2.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-prek/phl-prek-eligible-2.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-prek/phl-prek-eligible-2.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-prek/phl-prek-eligible.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-prek/phl-prek-eligible.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-prek/phl-prek-eligible.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-prek/phl-prek-eligible.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-property-tax-relief/_info.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-property-tax-relief/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-property-tax-relief/_info.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-property-tax-relief/_info.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-property-tax-relief/blank.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-property-tax-relief/blank.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-property-tax-relief/blank.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-property-tax-relief/blank.json diff --git a/thunder-tests/collections/all-tests/benefitsphl-property-tax-relief/phl-property-tax-relief.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphl-property-tax-relief/phl-property-tax-relief.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphl-property-tax-relief/phl-property-tax-relief.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphl-property-tax-relief/phl-property-tax-relief.json diff --git a/thunder-tests/collections/all-tests/benefitsphllitf/1-hh-eligibilecurrent-mo-income-not.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/1-hh-eligibilecurrent-mo-income-not.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphllitf/1-hh-eligibilecurrent-mo-income-not.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/1-hh-eligibilecurrent-mo-income-not.json diff --git a/thunder-tests/collections/all-tests/benefitsphllitf/1-hh-ineligibilecurrent-mo-income-n.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/1-hh-ineligibilecurrent-mo-income-n.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphllitf/1-hh-ineligibilecurrent-mo-income-n.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/1-hh-ineligibilecurrent-mo-income-n.json diff --git a/thunder-tests/collections/all-tests/benefitsphllitf/2-hh-eligibilecurrent-mo-income-mar.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/2-hh-eligibilecurrent-mo-income-mar.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphllitf/2-hh-eligibilecurrent-mo-income-mar.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/2-hh-eligibilecurrent-mo-income-mar.json diff --git a/thunder-tests/collections/all-tests/benefitsphllitf/2-hh-ineligibilecurrent-mo-income-m.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/2-hh-ineligibilecurrent-mo-income-m.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphllitf/2-hh-ineligibilecurrent-mo-income-m.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/2-hh-ineligibilecurrent-mo-income-m.json diff --git a/thunder-tests/collections/all-tests/benefitsphllitf/3-hh-eligibilecurrent-mo-income-not.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/3-hh-eligibilecurrent-mo-income-not.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphllitf/3-hh-eligibilecurrent-mo-income-not.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/3-hh-eligibilecurrent-mo-income-not.json diff --git a/thunder-tests/collections/all-tests/benefitsphllitf/3-hh-eligibilecurrent-yr-income-mar.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/3-hh-eligibilecurrent-yr-income-mar.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphllitf/3-hh-eligibilecurrent-yr-income-mar.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/3-hh-eligibilecurrent-yr-income-mar.json diff --git a/thunder-tests/collections/all-tests/benefitsphllitf/3-hh-ineligibilecurrent-mo-income-m.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/3-hh-ineligibilecurrent-mo-income-m.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphllitf/3-hh-ineligibilecurrent-mo-income-m.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/3-hh-ineligibilecurrent-mo-income-m.json diff --git a/thunder-tests/collections/all-tests/benefitsphllitf/3-hh-ineligibilecurrent-mo-income-n.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/3-hh-ineligibilecurrent-mo-income-n.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphllitf/3-hh-ineligibilecurrent-mo-income-n.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/3-hh-ineligibilecurrent-mo-income-n.json diff --git a/thunder-tests/collections/all-tests/benefitsphllitf/4-hh-eligiblemarried-total-primary.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/4-hh-eligiblemarried-total-primary.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphllitf/4-hh-eligiblemarried-total-primary.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/4-hh-eligiblemarried-total-primary.json diff --git a/thunder-tests/collections/all-tests/benefitsphllitf/4-hh-eligiblenot-married-total-prim.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/4-hh-eligiblenot-married-total-prim.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphllitf/4-hh-eligiblenot-married-total-prim.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/4-hh-eligiblenot-married-total-prim.json diff --git a/thunder-tests/collections/all-tests/benefitsphllitf/4-hh-ineligiblemarried-total-primar.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/4-hh-ineligiblemarried-total-primar.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphllitf/4-hh-ineligiblemarried-total-primar.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/4-hh-ineligiblemarried-total-primar.json diff --git a/thunder-tests/collections/all-tests/benefitsphllitf/4-hh-inligiblenot-married-total-pri.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/4-hh-inligiblenot-married-total-pri.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphllitf/4-hh-inligiblenot-married-total-pri.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/4-hh-inligiblenot-married-total-pri.json diff --git a/thunder-tests/collections/all-tests/benefitsphllitf/_info.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphllitf/_info.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/_info.json diff --git a/thunder-tests/collections/all-tests/benefitsphllitf/already-enrolledlitf.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/already-enrolledlitf.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphllitf/already-enrolledlitf.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/already-enrolledlitf.json diff --git a/thunder-tests/collections/all-tests/benefitsphllitf/blank.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/blank.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphllitf/blank.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/blank.json diff --git a/thunder-tests/collections/all-tests/benefitsphllitf/not-a-homeownerlitf.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/not-a-homeownerlitf.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphllitf/not-a-homeownerlitf.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/not-a-homeownerlitf.json diff --git a/thunder-tests/collections/all-tests/benefitsphllitf/not-in-phillylitf.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/not-in-phillylitf.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphllitf/not-in-phillylitf.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/not-in-phillylitf.json diff --git a/thunder-tests/collections/all-tests/benefitsphllitf/not-owner-occupantlitf.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/not-owner-occupantlitf.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphllitf/not-owner-occupantlitf.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphllitf/not-owner-occupantlitf.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-eligibileage-50-and-widow-equa.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-eligibileage-50-and-widow-equa.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-eligibileage-50-and-widow-equa.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-eligibileage-50-and-widow-equa.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-eligibilecurrent-mo-income-not.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-eligibilecurrent-mo-income-not.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-eligibilecurrent-mo-income-not.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-eligibilecurrent-mo-income-not.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-ineligibile-age-49.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-ineligibile-age-49.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-ineligibile-age-49.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-ineligibile-age-49.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-ineligibile-age-50-but-widow-e.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-ineligibile-age-50-but-widow-e.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-ineligibile-age-50-but-widow-e.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-ineligibile-age-50-but-widow-e.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-ineligibilecurrent-mo-income-n.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-ineligibilecurrent-mo-income-n.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-ineligibilecurrent-mo-income-n.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-ineligibilecurrent-mo-income-n.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-null-eligibilityage-50-and-wid.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-null-eligibilityage-50-and-wid.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-null-eligibilityage-50-and-wid.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/1-hh-null-eligibilityage-50-and-wid.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/2-hh-eligibilecurrent-mo-income-mar.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/2-hh-eligibilecurrent-mo-income-mar.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/2-hh-eligibilecurrent-mo-income-mar.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/2-hh-eligibilecurrent-mo-income-mar.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/2-hh-ineligibilecurrent-mo-income-m.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/2-hh-ineligibilecurrent-mo-income-m.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/2-hh-ineligibilecurrent-mo-income-m.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/2-hh-ineligibilecurrent-mo-income-m.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/3-hh-eligibilecurrent-mo-income-not.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/3-hh-eligibilecurrent-mo-income-not.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/3-hh-eligibilecurrent-mo-income-not.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/3-hh-eligibilecurrent-mo-income-not.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/3-hh-eligibilecurrent-yr-income-mar.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/3-hh-eligibilecurrent-yr-income-mar.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/3-hh-eligibilecurrent-yr-income-mar.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/3-hh-eligibilecurrent-yr-income-mar.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/3-hh-ineligibilecurrent-mo-income-m.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/3-hh-ineligibilecurrent-mo-income-m.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/3-hh-ineligibilecurrent-mo-income-m.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/3-hh-ineligibilecurrent-mo-income-m.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/3-hh-ineligibilecurrent-mo-income-n.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/3-hh-ineligibilecurrent-mo-income-n.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/3-hh-ineligibilecurrent-mo-income-n.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/3-hh-ineligibilecurrent-mo-income-n.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-eligibility-null-no-income-lis.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-eligibility-null-no-income-lis.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-eligibility-null-no-income-lis.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-eligibility-null-no-income-lis.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-eligiblemarried-total-primary.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-eligiblemarried-total-primary.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-eligiblemarried-total-primary.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-eligiblemarried-total-primary.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-eligiblenot-married-total-prim.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-eligiblenot-married-total-prim.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-eligiblenot-married-total-prim.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-eligiblenot-married-total-prim.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-ineligiblemarried-total-primar.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-ineligiblemarried-total-primar.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-ineligiblemarried-total-primar.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-ineligiblemarried-total-primar.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-ineligiblenot-age-eligible-for.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-ineligiblenot-age-eligible-for.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-ineligiblenot-age-eligible-for.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-ineligiblenot-age-eligible-for.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-ineligiblenot-married-total-pr.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-ineligiblenot-married-total-pr.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-ineligiblenot-married-total-pr.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/4-hh-ineligiblenot-married-total-pr.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/_info.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/_info.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/_info.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/already-enrolledsctf.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/already-enrolledsctf.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/already-enrolledsctf.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/already-enrolledsctf.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/blank.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/blank.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/blank.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/blank.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/not-a-homeownersctf.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/not-a-homeownersctf.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/not-a-homeownersctf.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/not-a-homeownersctf.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/not-in-phillysctf.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/not-in-phillysctf.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/not-in-phillysctf.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/not-in-phillysctf.json diff --git a/thunder-tests/collections/all-tests/benefitsphlsctf/not-owner-occupantsctf.json b/dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/not-owner-occupantsctf.json similarity index 100% rename from thunder-tests/collections/all-tests/benefitsphlsctf/not-owner-occupantsctf.json rename to dmn-library/thunder-tests/collections/all-tests/benefitsphlsctf/not-owner-occupantsctf.json diff --git a/thunder-tests/collections/all-tests/housingdmn/_info.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/_info.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/_info.json diff --git a/thunder-tests/collections/all-tests/housingdmn/homeowner-eligible/_info.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/homeowner-eligible/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/homeowner-eligible/_info.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/homeowner-eligible/_info.json diff --git a/thunder-tests/collections/all-tests/housingdmn/homeowner-eligible/eligible-homeowner.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/homeowner-eligible/eligible-homeowner.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/homeowner-eligible/eligible-homeowner.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/homeowner-eligible/eligible-homeowner.json diff --git a/thunder-tests/collections/all-tests/housingdmn/homeowner-eligible/has-equitable-interest-in-property.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/homeowner-eligible/has-equitable-interest-in-property.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/homeowner-eligible/has-equitable-interest-in-property.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/homeowner-eligible/has-equitable-interest-in-property.json diff --git a/thunder-tests/collections/all-tests/housingdmn/homeowner-eligible/not-a-homeowner-and-no-equitable-interset.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/homeowner-eligible/not-a-homeowner-and-no-equitable-interset.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/homeowner-eligible/not-a-homeowner-and-no-equitable-interset.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/homeowner-eligible/not-a-homeowner-and-no-equitable-interset.json diff --git a/thunder-tests/collections/all-tests/housingdmn/loop-tax-assessment-eligible/_info.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/loop-tax-assessment-eligible/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/loop-tax-assessment-eligible/_info.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/loop-tax-assessment-eligible/_info.json diff --git a/thunder-tests/collections/all-tests/housingdmn/loop-tax-assessment-eligible/eligibleapply-for-loop.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/loop-tax-assessment-eligible/eligibleapply-for-loop.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/loop-tax-assessment-eligible/eligibleapply-for-loop.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/loop-tax-assessment-eligible/eligibleapply-for-loop.json diff --git a/thunder-tests/collections/all-tests/housingdmn/loop-tax-assessment-eligible/inel-for-loopassessement-values.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/loop-tax-assessment-eligible/inel-for-loopassessement-values.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/loop-tax-assessment-eligible/inel-for-loopassessement-values.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/loop-tax-assessment-eligible/inel-for-loopassessement-values.json diff --git a/thunder-tests/collections/all-tests/housingdmn/loop-tax-assessment-eligible/tax-assessment-eligible-null.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/loop-tax-assessment-eligible/tax-assessment-eligible-null.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/loop-tax-assessment-eligible/tax-assessment-eligible-null.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/loop-tax-assessment-eligible/tax-assessment-eligible-null.json diff --git a/thunder-tests/collections/all-tests/housingdmn/no-ten-year-tax-abatement/_info.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/no-ten-year-tax-abatement/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/no-ten-year-tax-abatement/_info.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/no-ten-year-tax-abatement/_info.json diff --git a/thunder-tests/collections/all-tests/housingdmn/no-ten-year-tax-abatement/eligible-does-not-have-10-yr-tax-abatement.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/no-ten-year-tax-abatement/eligible-does-not-have-10-yr-tax-abatement.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/no-ten-year-tax-abatement/eligible-does-not-have-10-yr-tax-abatement.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/no-ten-year-tax-abatement/eligible-does-not-have-10-yr-tax-abatement.json diff --git a/thunder-tests/collections/all-tests/housingdmn/no-ten-year-tax-abatement/ineligible-due-to-10-yr-tax-abatement.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/no-ten-year-tax-abatement/ineligible-due-to-10-yr-tax-abatement.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/no-ten-year-tax-abatement/ineligible-due-to-10-yr-tax-abatement.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/no-ten-year-tax-abatement/ineligible-due-to-10-yr-tax-abatement.json diff --git a/thunder-tests/collections/all-tests/housingdmn/not-tax-delinquent/_info.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/not-tax-delinquent/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/not-tax-delinquent/_info.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/not-tax-delinquent/_info.json diff --git a/thunder-tests/collections/all-tests/housingdmn/not-tax-delinquent/eligible-for-loopnot-tax-delinquent.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/not-tax-delinquent/eligible-for-loopnot-tax-delinquent.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/not-tax-delinquent/eligible-for-loopnot-tax-delinquent.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/not-tax-delinquent/eligible-for-loopnot-tax-delinquent.json diff --git a/thunder-tests/collections/all-tests/housingdmn/not-tax-delinquent/inel-for-looptax-delinquent.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/not-tax-delinquent/inel-for-looptax-delinquent.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/not-tax-delinquent/inel-for-looptax-delinquent.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/not-tax-delinquent/inel-for-looptax-delinquent.json diff --git a/thunder-tests/collections/all-tests/housingdmn/owner-occupant/_info.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/owner-occupant/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/owner-occupant/_info.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/owner-occupant/_info.json diff --git a/thunder-tests/collections/all-tests/housingdmn/owner-occupant/not-an-owner-occupant.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/owner-occupant/not-an-owner-occupant.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/owner-occupant/not-an-owner-occupant.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/owner-occupant/not-an-owner-occupant.json diff --git a/thunder-tests/collections/all-tests/housingdmn/owner-occupant/owner-occupant.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/owner-occupant/owner-occupant.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/owner-occupant/owner-occupant.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/owner-occupant/owner-occupant.json diff --git a/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/_info.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/_info.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/_info.json diff --git a/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/eligiblephilly-owner-occupant-homeo-3.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/eligiblephilly-owner-occupant-homeo-3.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/eligiblephilly-owner-occupant-homeo-3.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/eligiblephilly-owner-occupant-homeo-3.json diff --git a/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/eligiblephilly-owner-occupant-homeo.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/eligiblephilly-owner-occupant-homeo.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/eligiblephilly-owner-occupant-homeo.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/eligiblephilly-owner-occupant-homeo.json diff --git a/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/elligiblenot-a-homeownerhas-equitab-2.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/elligiblenot-a-homeownerhas-equitab-2.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/elligiblenot-a-homeownerhas-equitab-2.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/elligiblenot-a-homeownerhas-equitab-2.json diff --git a/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/ineligiblenot-a-homeownerno-equitab.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/ineligiblenot-a-homeownerno-equitab.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/ineligiblenot-a-homeownerno-equitab.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/ineligiblenot-a-homeownerno-equitab.json diff --git a/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/ineligiblenot-in-philly.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/ineligiblenot-in-philly.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/ineligiblenot-in-philly.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/ineligiblenot-in-philly.json diff --git a/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/ineligiblephilly-homeownernot-owner.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/ineligiblephilly-homeownernot-owner.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/ineligiblephilly-homeownernot-owner.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/philly-owner-occupant-homeowner/ineligiblephilly-homeownernot-owner.json diff --git a/thunder-tests/collections/all-tests/housingdmn/tax-delinquent/_info.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/tax-delinquent/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/tax-delinquent/_info.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/tax-delinquent/_info.json diff --git a/thunder-tests/collections/all-tests/housingdmn/tax-delinquent/elig-for-phl-oopatax-delinquent.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/tax-delinquent/elig-for-phl-oopatax-delinquent.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/tax-delinquent/elig-for-phl-oopatax-delinquent.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/tax-delinquent/elig-for-phl-oopatax-delinquent.json diff --git a/thunder-tests/collections/all-tests/housingdmn/tax-delinquent/inelig-for-phl-oopanot-tax-delinque.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/tax-delinquent/inelig-for-phl-oopanot-tax-delinque.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/tax-delinquent/inelig-for-phl-oopanot-tax-delinque.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/tax-delinquent/inelig-for-phl-oopanot-tax-delinque.json diff --git a/thunder-tests/collections/all-tests/housingdmn/ten-ormore-years-owner-occupant/_info.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/ten-ormore-years-owner-occupant/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/ten-ormore-years-owner-occupant/_info.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/ten-ormore-years-owner-occupant/_info.json diff --git a/thunder-tests/collections/all-tests/housingdmn/ten-ormore-years-owner-occupant/eligible-for-loop-10-year-owner-occupant.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/ten-ormore-years-owner-occupant/eligible-for-loop-10-year-owner-occupant.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/ten-ormore-years-owner-occupant/eligible-for-loop-10-year-owner-occupant.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/ten-ormore-years-owner-occupant/eligible-for-loop-10-year-owner-occupant.json diff --git a/thunder-tests/collections/all-tests/housingdmn/ten-ormore-years-owner-occupant/inel-for-loopless-than-10-year-owner-occupant.json b/dmn-library/thunder-tests/collections/all-tests/housingdmn/ten-ormore-years-owner-occupant/inel-for-loopless-than-10-year-owner-occupant.json similarity index 100% rename from thunder-tests/collections/all-tests/housingdmn/ten-ormore-years-owner-occupant/inel-for-loopless-than-10-year-owner-occupant.json rename to dmn-library/thunder-tests/collections/all-tests/housingdmn/ten-ormore-years-owner-occupant/inel-for-loopless-than-10-year-owner-occupant.json diff --git a/thunder-tests/collections/all-tests/income-dmn/_info.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/_info.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/_info.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/1-hh-eligiblecurrent-mo-income-per.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/1-hh-eligiblecurrent-mo-income-per.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/1-hh-eligiblecurrent-mo-income-per.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/1-hh-eligiblecurrent-mo-income-per.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/1-hh-eligibletotal-hh-current-mo-in-2.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/1-hh-eligibletotal-hh-current-mo-in-2.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/1-hh-eligibletotal-hh-current-mo-in-2.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/1-hh-eligibletotal-hh-current-mo-in-2.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/3-hh-eligiblecurrent-mo-income-per-2.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/3-hh-eligiblecurrent-mo-income-per-2.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/3-hh-eligiblecurrent-mo-income-per-2.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/3-hh-eligiblecurrent-mo-income-per-2.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/3-hh-ineligiblecurrent-mo-income-pe-2.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/3-hh-ineligiblecurrent-mo-income-pe-2.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/3-hh-ineligiblecurrent-mo-income-pe-2.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/3-hh-ineligiblecurrent-mo-income-pe-2.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/4-hh-eligiblecurrent-mo-income-tota.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/4-hh-eligiblecurrent-mo-income-tota.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/4-hh-eligiblecurrent-mo-income-tota.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/4-hh-eligiblecurrent-mo-income-tota.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/4-hh-ineligiblecurrent-mo-income-to.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/4-hh-ineligiblecurrent-mo-income-to.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/4-hh-ineligiblecurrent-mo-income-to.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/4-hh-ineligiblecurrent-mo-income-to.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/5-hh-eligibletotal-current-hh-incom.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/5-hh-eligibletotal-current-hh-incom.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/5-hh-eligibletotal-current-hh-incom.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/5-hh-eligibletotal-current-hh-incom.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/5-hh-ineligibletotal-current-hh-inc.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/5-hh-ineligibletotal-current-hh-inc.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/5-hh-ineligibletotal-current-hh-inc.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/5-hh-ineligibletotal-current-hh-inc.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/_info.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/_info.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-current-gross-monthly-income/_info.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/2-hh-eligiblemarried-prev-year-annu.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/2-hh-eligiblemarried-prev-year-annu.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/2-hh-eligiblemarried-prev-year-annu.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/2-hh-eligiblemarried-prev-year-annu.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/2-hh-ineligiblesingle-current-mo-in.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/2-hh-ineligiblesingle-current-mo-in.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/2-hh-ineligiblesingle-current-mo-in.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/2-hh-ineligiblesingle-current-mo-in.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/3-hh-eligiblesingle-current-mo-inco.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/3-hh-eligiblesingle-current-mo-inco.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/3-hh-eligiblesingle-current-mo-inco.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/3-hh-eligiblesingle-current-mo-inco.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/6-hh-eligcurrent-montly-primary-and-2.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/6-hh-eligcurrent-montly-primary-and-2.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/6-hh-eligcurrent-montly-primary-and-2.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/6-hh-eligcurrent-montly-primary-and-2.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/_info.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/_info.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-current-monthl/_info.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/2-hh-eligiblemarried-prev-year-annu.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/2-hh-eligiblemarried-prev-year-annu.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/2-hh-eligiblemarried-prev-year-annu.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/2-hh-eligiblemarried-prev-year-annu.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/2-hh-eligiblesingle-prev-year-annua.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/2-hh-eligiblesingle-prev-year-annua.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/2-hh-eligiblesingle-prev-year-annua.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/2-hh-eligiblesingle-prev-year-annua.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/2-hh-ineligiblemarried-prev-year-an.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/2-hh-ineligiblemarried-prev-year-an.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/2-hh-ineligiblemarried-prev-year-an.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/2-hh-ineligiblemarried-prev-year-an.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/2-hh-ineligiblesingle-prev-year-ann-2.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/2-hh-ineligiblesingle-prev-year-ann-2.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/2-hh-ineligiblesingle-prev-year-ann-2.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/2-hh-ineligiblesingle-prev-year-ann-2.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/3-hh-eligiblemarried-prev-yr-annual.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/3-hh-eligiblemarried-prev-yr-annual.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/3-hh-eligiblemarried-prev-yr-annual.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/3-hh-eligiblemarried-prev-yr-annual.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/3-hh-eligiblesingle-prev-yr-annual.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/3-hh-eligiblesingle-prev-yr-annual.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/3-hh-eligiblesingle-prev-yr-annual.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/3-hh-eligiblesingle-prev-yr-annual.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/3-hh-ineligiblemarried-prev-yr-annu.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/3-hh-ineligiblemarried-prev-yr-annu.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/3-hh-ineligiblemarried-prev-yr-annu.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/3-hh-ineligiblemarried-prev-yr-annu.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/3-hh-ineligiblesingle-prev-yr-annua.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/3-hh-ineligiblesingle-prev-yr-annua.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/3-hh-ineligiblesingle-prev-yr-annua.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/3-hh-ineligiblesingle-prev-yr-annua.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/6-hh-eligprevious-year-primary-and.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/6-hh-eligprevious-year-primary-and.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/6-hh-eligprevious-year-primary-and.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/6-hh-eligprevious-year-primary-and.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/_info.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/_info.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-marital-status-previous-annua/_info.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/2-hh-eligibleprevious-yr-annual-inc.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/2-hh-eligibleprevious-yr-annual-inc.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/2-hh-eligibleprevious-yr-annual-inc.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/2-hh-eligibleprevious-yr-annual-inc.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/2-hh-ineligibleprevious-yr-annual-i.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/2-hh-ineligibleprevious-yr-annual-i.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/2-hh-ineligibleprevious-yr-annual-i.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/2-hh-ineligibleprevious-yr-annual-i.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/3-hh-eligibleprev-yr-annual-income.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/3-hh-eligibleprev-yr-annual-income.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/3-hh-eligibleprev-yr-annual-income.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/3-hh-eligibleprev-yr-annual-income.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/3-hh-inligibleprev-yr-annual-income.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/3-hh-inligibleprev-yr-annual-income.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/3-hh-inligibleprev-yr-annual-income.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/3-hh-inligibleprev-yr-annual-income.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/6-hh-eligibletotal-prev-annual-hh-i.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/6-hh-eligibletotal-prev-annual-hh-i.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/6-hh-eligibletotal-prev-annual-hh-i.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/6-hh-eligibletotal-prev-annual-hh-i.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/6-hh-ineligibletotal-prev-annual-hh.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/6-hh-ineligibletotal-prev-annual-hh.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/6-hh-ineligibletotal-prev-annual-hh.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/6-hh-ineligibletotal-prev-annual-hh.json diff --git a/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/_info.json b/dmn-library/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/_info.json rename to dmn-library/thunder-tests/collections/all-tests/income-dmn/under-previous-annual-gross-income/_info.json diff --git a/thunder-tests/collections/all-tests/locationdmn/_info.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/_info.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/_info.json diff --git a/thunder-tests/collections/all-tests/locationdmn/lives-inpa/_info.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/lives-inpa/_info.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/_info.json diff --git a/thunder-tests/collections/all-tests/locationdmn/lives-inpa/blank.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/blank.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/lives-inpa/blank.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/blank.json diff --git a/thunder-tests/collections/all-tests/locationdmn/lives-inpa/bogus-zip-code.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/bogus-zip-code.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/lives-inpa/bogus-zip-code.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/bogus-zip-code.json diff --git a/thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-fips-in-pa.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-fips-in-pa.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-fips-in-pa.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-fips-in-pa.json diff --git a/thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-fips-is-bogus.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-fips-is-bogus.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-fips-is-bogus.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-fips-is-bogus.json diff --git a/thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-fips-not-in-pa.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-fips-not-in-pa.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-fips-not-in-pa.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-fips-not-in-pa.json diff --git a/thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-name-and-zip-in-pa.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-name-and-zip-in-pa.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-name-and-zip-in-pa.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-name-and-zip-in-pa.json diff --git a/thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-name-and-zip-not-in-pa.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-name-and-zip-not-in-pa.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-name-and-zip-not-in-pa.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-name-and-zip-not-in-pa.json diff --git a/thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-name-mispelled-and-zip-in-pa-wont-work.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-name-mispelled-and-zip-in-pa-wont-work.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-name-mispelled-and-zip-in-pa-wont-work.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/county-name-mispelled-and-zip-in-pa-wont-work.json diff --git a/thunder-tests/collections/all-tests/locationdmn/lives-inpa/pa-is-given.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/pa-is-given.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/lives-inpa/pa-is-given.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/pa-is-given.json diff --git a/thunder-tests/collections/all-tests/locationdmn/lives-inpa/zip-in-pa.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/zip-in-pa.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/lives-inpa/zip-in-pa.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inpa/zip-in-pa.json diff --git a/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/_info.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/_info.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/_info.json diff --git a/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/ambiguous-philly-zip.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/ambiguous-philly-zip.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/ambiguous-philly-zip.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/ambiguous-philly-zip.json diff --git a/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/definitely-philly-zip.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/definitely-philly-zip.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/definitely-philly-zip.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/definitely-philly-zip.json diff --git a/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/empty-post-data.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/empty-post-data.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/empty-post-data.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/empty-post-data.json diff --git a/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/inputs-null.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/inputs-null.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/inputs-null.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/inputs-null.json diff --git a/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/non-existent-zip.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/non-existent-zip.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/non-existent-zip.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/non-existent-zip.json diff --git a/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/non-philly-zip.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/non-philly-zip.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/non-philly-zip.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/non-philly-zip.json diff --git a/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/null-zip.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/null-zip.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/null-zip.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/lives-inphiladelphia-pa/null-zip.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/_info.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inpa/_info.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/_info.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/blank.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/blank.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inpa/blank.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/blank.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/bogus-state-is-given.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/bogus-state-is-given.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inpa/bogus-state-is-given.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/bogus-state-is-given.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/bogus-zip-code.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/bogus-zip-code.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inpa/bogus-zip-code.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/bogus-zip-code.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-fips-in-pa.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-fips-in-pa.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-fips-in-pa.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-fips-in-pa.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-fips-is-bogus.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-fips-is-bogus.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-fips-is-bogus.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-fips-is-bogus.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-fips-not-in-pa.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-fips-not-in-pa.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-fips-not-in-pa.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-fips-not-in-pa.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-name-and-zip-in-pa.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-name-and-zip-in-pa.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-name-and-zip-in-pa.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-name-and-zip-in-pa.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-name-and-zip-not-in-pa.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-name-and-zip-not-in-pa.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-name-and-zip-not-in-pa.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-name-and-zip-not-in-pa.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-name-mispelled-and-zip-in-pa-wont-work.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-name-mispelled-and-zip-in-pa-wont-work.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-name-mispelled-and-zip-in-pa-wont-work.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/county-name-mispelled-and-zip-in-pa-wont-work.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/other-state-is-given.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/other-state-is-given.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inpa/other-state-is-given.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/other-state-is-given.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/pa-is-given.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/pa-is-given.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inpa/pa-is-given.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/pa-is-given.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/zip-in-pa.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/zip-in-pa.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inpa/zip-in-pa.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inpa/zip-in-pa.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/_info.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/_info.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/_info.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/_info.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/ambigious-philly-zip.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/ambigious-philly-zip.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/ambigious-philly-zip.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/ambigious-philly-zip.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/ambigious-zip-and-county-name-match-philadelphia.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/ambigious-zip-and-county-name-match-philadelphia.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/ambigious-zip-and-county-name-match-philadelphia.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/ambigious-zip-and-county-name-match-philadelphia.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/ambigious-zip-and-state-maybe-philadelphia.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/ambigious-zip-and-state-maybe-philadelphia.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/ambigious-zip-and-state-maybe-philadelphia.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/ambigious-zip-and-state-maybe-philadelphia.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-fips-is-not-in-philadelphia.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-fips-is-not-in-philadelphia.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-fips-is-not-in-philadelphia.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-fips-is-not-in-philadelphia.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-fips-is-philadelphia.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-fips-is-philadelphia.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-fips-is-philadelphia.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-fips-is-philadelphia.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-name-and-state-match-philadelphia.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-name-and-state-match-philadelphia.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-name-and-state-match-philadelphia.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-name-and-state-match-philadelphia.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-name-and-state-mismatch-philadelphia.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-name-and-state-mismatch-philadelphia.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-name-and-state-mismatch-philadelphia.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-name-and-state-mismatch-philadelphia.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-name-mispelled.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-name-mispelled.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-name-mispelled.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/county-name-mispelled.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/definitely-philly-zip.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/definitely-philly-zip.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/definitely-philly-zip.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/definitely-philly-zip.json diff --git a/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/non-philly-zip.json b/dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/non-philly-zip.json similarity index 100% rename from thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/non-philly-zip.json rename to dmn-library/thunder-tests/collections/all-tests/locationdmn/may-live-inphiladelphia-pa/non-philly-zip.json diff --git a/thunder-tests/thunderLocalEnvironment.json b/dmn-library/thunder-tests/thunderLocalEnvironment.json similarity index 100% rename from thunder-tests/thunderLocalEnvironment.json rename to dmn-library/thunder-tests/thunderLocalEnvironment.json diff --git a/firebase.json b/firebase.json new file mode 100644 index 0000000..8e1d725 --- /dev/null +++ b/firebase.json @@ -0,0 +1,34 @@ +{ + "hosting": [ + { + "target": "screener-frontend", + "public": "./screener-frontend/dist", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ], + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + }, + { + "target": "builder-frontend", + "public": "./builder-frontend/dist", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ], + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + } + ] +} \ No newline at end of file diff --git a/firebase.old.json b/firebase.old.json new file mode 100644 index 0000000..8e1d725 --- /dev/null +++ b/firebase.old.json @@ -0,0 +1,34 @@ +{ + "hosting": [ + { + "target": "screener-frontend", + "public": "./screener-frontend/dist", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ], + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + }, + { + "target": "builder-frontend", + "public": "./builder-frontend/dist", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ], + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + } + ] +} \ No newline at end of file diff --git a/model-library/deploy-models.py b/model-library/deploy-models.py new file mode 100644 index 0000000..9c5eaa1 --- /dev/null +++ b/model-library/deploy-models.py @@ -0,0 +1,37 @@ +import os +from google.cloud import storage + +# Configuration +LOCAL_DIR = "./dmn/" +BUCKET_NAME = "benefits-decision-toolkit.firebasestorage.app" + +# Map of local filenames to their corresponding GCS paths +GCS_PATH_MAP = { + "compare.dmn": "model_repository/benefits-decision-toolkit/compare/1.0.0/compare.dmn", + "utility.dmn": "model_repository/benefits-decision-toolkit/utility/1.0.0/utility.dmn", + "age.dmn": "model_repository/benefits-decision-toolkit/age/1.0.0/age.dmn", + "enrollments.dmn": "model_repository/benefits-decision-toolkit/enrollments/1.0.0/enrollments.dmn", + "housing.dmn": "model_repository/benefits-decision-toolkit/housing/1.0.0/housing.dmn", + "income.dmn": "model_repository/benefits-decision-toolkit/income/1.0.0/income.dmn", + "location.dmn": "model_repository/benefits-decision-toolkit/location/1.0.0/location.dmn", +} + + +def upload_dmn_files(local_dir, bucket_name, path_map): + client = storage.Client() + bucket = client.bucket(bucket_name) + + for local_file, gcs_path in path_map.items(): + local_path = os.path.join(local_dir, local_file) + + if not os.path.exists(local_path): + print(f"[WARNING] File not found: {local_path}, skipping.") + continue + + blob = bucket.blob(gcs_path) + blob.upload_from_filename(local_path) + print(f"[INFO] Uploaded {local_file} to gs://{bucket_name}/{gcs_path}") + + +if __name__ == "__main__": + upload_dmn_files(LOCAL_DIR, BUCKET_NAME, GCS_PATH_MAP) diff --git a/model-library/dmn/age.dmn b/model-library/dmn/age.dmn new file mode 100644 index 0000000..8c7de20 --- /dev/null +++ b/model-library/dmn/age.dmn @@ -0,0 +1,745 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + function(args) as of date(date of birth: args.date of birth, comparison date: args.comparison date) + + + + + + function(args) as of today(date of birth: args.date of birth) + + + + + + function(args) end of last year(date of birth: args.date of birth) + + + + + + + + get value(BKMs, bkmName)(args: bkmArguments) + + + + + result + + + + + + + + + + + + + + + + + + + as of date(date of birth, today()) + + + + + + + + + + + + + as of date(date of birth, date(string(today().year-1)+"-12-31")) + + + + + + + + + + + + + + + + + + + + for person in inputs.people return + if person.dateOfBirth = null then + null + else + as of date(date(person.dateOfBirth), date(string(today().year)+"-09-01")) in [3..4] + + + + + + if inputs.people = null or count(inputs.people) = 0 or (every person in threeAndFourYearOlds satisfies person = null) then null +else nn any(threeAndFourYearOlds) + + + + + result + + + + + + + + + + + + + + + + + + + + + + +for person in inputs.people return + if person.dateOfBirth = null then + null + else as of today(date(person.dateOfBirth)) in [60..120] + + + + + + if (inputs.people = null or count(inputs.people) = 0 or (every person in ages satisfies person = null)) then null +else + (if not(nn any(ages)) and (some person in ages satisfies person = null) then null else nn any(ages)) + + + + + result + + + + + + + + + + + + + + + if inputs.dateOfBirth != null then + as of date(date(inputs.dateOfBirth), date("2025-12-31")) in [65..120] +else if inputs.people != null and count(inputs.people[primary=true]) > 0 and inputs.people[primary=true][1].dateOfBirth != null then + as of date(date(inputs.people[primary=true][1].dateOfBirth), date("2025-12-31")) in [65..120] +else + null + + + + + + + + + + if date of birth = null or comparison date = null then + -1 +else + years and months duration(date(date of birth), date(comparison date)).years + + + + + + + + + + + + + + if inputs.people != null and count(inputs.people[spouseOfPrimary=true]) > 0 and inputs.people[spouseOfPrimary=true][1].dateOfBirth != null then + as of date(date(inputs.people[spouseOfPrimary=true][1].dateOfBirth), date("2025-12-31")) in [65..120] +else + null + + + + + + + + + + + + + if inputs.dateOfBirth != null then + as of date(date(inputs.dateOfBirth), date("2025-12-31")) in [50..120] +else if inputs.people != null and count(inputs.people[primary=true]) > 0 and inputs.people[primary=true][1].dateOfBirth != null then + as of date(date(inputs.people[primary=true][1].dateOfBirth), date("2025-12-31")) in [50..120] +else + null + + + + + + + + + + if inputs.lateSpouseWasAtLeastSixtyFive != null then + inputs.lateSpouseWasAtLeastSixtyFive +else if inputs.people != null and count(inputs.people[primary=true]) > 0 then inputs.people[primary=true][1].lateSpouseWasAtLeastSixtyFive +else + null + + + + + + + + 50 + 120 + 857 + + + 50 + 192 + 595 + + + 595 + + + 595 + + + 595 + + + 857 + + + 857 + + + 348 + + + 50 + 348 + + + 597 + + + 50 + 597 + + + 50 + 262 + 738 + + + 738 + + + 738 + + + 738 + + + 50 + 230 + 1200 + + + 1200 + + + 1200 + + + 1200 + + + 1171 + + + + + 1171 + + + + + 1171 + + + + + 723 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/model-library/dmn/enrollments.dmn b/model-library/dmn/enrollments.dmn new file mode 100644 index 0000000..d3ba771 --- /dev/null +++ b/model-library/dmn/enrollments.dmn @@ -0,0 +1,1239 @@ + + + + + + string + + "phlOopa", "phlLowIncomeTaxFreeze", "phlHomesteadExemption" + + + + enrollmentKey + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + inputs.notEnrolledInPhlPreK + + + + + + notEnrolledInPreK + + + + + result + + + + + + + + + + + + + + + + + + + inputs.notEnrolledInPaSeniorFoodBox + + + + + + notEnrolledInPaSeniorFoodBox + + + + + result + + + + + + + + + + + + + + + not(enrolledInPhlLoop(inputs)) + + + + + + + + + + + + + not(enrolledInPhlHomesteadExemption(inputs)) + + + + + + + + + + + + + not(enrolledInPhlOopa(inputs)) + + + + + + + + + + + + + enrolledInBenefit(inputs.enrolledInPhlOopa, inputs.notEnrolledInPhlOopa, inputs.enrollments, "phlOopa") + + + + + + + + + + + + + not(enrolledInPhlLowIncomeTaxFreeze(inputs)) + + + + + + + + + + + + + enrolledInBenefit(inputs.enrolledInPhlHomesteadExemption, inputs.notEnrolledInPhlHomesteadExemption, inputs.enrollments, "phlHomesteadExemption") + + + + + + + + + + + + + not(enrolledInPhlSeniorCitizenTaxFreeze(inputs)) + + + + + + + + + + + + if enrolledValue != null then + enrolledValue +else if notEnrolledValue != null then + not(notEnrolledValue) +else if enrollmentsList != null then + list contains(enrollmentsList, enrollmentKey) +else + null + + + + + + + + + + + + + + enrolledInBenefit(inputs.enrolledInPhlLowIncomeTaxFreeze, inputs.notEnrolledInPhlLowIncomeTaxFreeze, inputs.enrollments, "phlLowIncomeTaxFreeze") + + + + + + + + + + + + + enrolledInBenefit(inputs.enrolledInPhlSeniorCitizenTaxFreeze, inputs.notEnrolledInPhlSeniorCitizenTaxFreeze, inputs.enrollments, "phlSeniorCitizenTaxFreeze") + + + + + + + + + + + + + enrolledInBenefit(inputs.enrolledInPhlLoop, inputs.notEnrolledInPhlLoop, inputs.enrollments, "phlLoop") + + + + + + + + + + + + + enrolledInBenefit(inputs.enrolledInPaPaceOrPnet, inputs.notEnrolledInPaPaceOrPnet, inputs.enrollments, "paPaceOrPnet") + + + + + + + + + + + + + not(enrolledInPaPaceOrPnet(inputs)) + + + + + + + + + + + + + enrolledInBenefit(inputs.enrolledInPaMedicaidRx, inputs.notEnrolledInPaMedicaidRx, inputs.enrollments, "paMedicaidRx") + + + + + + + + + + + + + not(enrolledInPaMedicaidRx(inputs)) + + + + + + + + + + + + + + + + notEnrolledInPaPaceOrPnet(inputs) and notEnrolledInPaMedicaidRx(inputs) + + + + + + + + + + notEnrolledInPaPaceOrPnetAndNotEnrolledInPaMedicaidRxChecks + + + + + + + + 50 + 202 + 355 + + + 355 + + + 355 + + + 355 + + + 50 + 211 + 350 + + + 350 + + + 350 + + + 350 + + + 572 + + + 481 + + + 534 + + + 626 + + + 506 + + + 513 + + + 404 + + + + + + + 1237 + + + + + 1187 + + + + + 1398 + + + + + 946 + + + + + 465 + + + 661 + + + 481 + + + 725 + + + 584 + + + 809 + + + 50 + 809 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/model-library/dmn/housing.dmn b/model-library/dmn/housing.dmn new file mode 100644 index 0000000..f7c3e8c --- /dev/null +++ b/model-library/dmn/housing.dmn @@ -0,0 +1,881 @@ + + + + + + + + + number + + + number + + + + taxAssessment + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + homeownerEligibility + + + + + + + + + + + + + 2025 + + + + + + inputs.homeowner + + + + + + inputs.equitableInterestInProperty + + + + + + homeowner or equitableInterestInProperty + + + + + result + + + + + + + + + + + + + + + + + + + inputs.tenYearTaxAbatement + + + + + + not(tenYearTaxAbatement) + + + + + result + + + + + + + + + + + + + + + inputs.ownerOccupant + + + + + + ownerOccupant + + + + + result + + + + + + + + + + + + + + + inputs.notTaxDelinquent + + + + + + notTaxDelinquent + + + + + result + + + + + + + + + + + + + + + inputs.tenOrMoreYearsOwnerOccupant + + + + + + tenOrMoreYearsOwnerOccupant + + + + + result + + + + + + + + + + + + + + + + + + notTaxDelinquent(inputs) + + + + + + not(notTaxDelinquent) + + + + + + taxDelinquent + + + + + result + + + + + + + + + + + + phillyOwnerOccupantHomeownerChecks + + + + + + + + + + + + + + + + + + + homeownerEligible(inputs) and ownerOccupant(inputs) and Location.livesInPhiladelphiaPa(inputs) + + + + + + + + + + notTaxDelinquentOrOnPhlOopaChecks + + + + + + + + + + + + + + + + notTaxDelinquent(inputs) or Enrollments.enrolledInPhlOopa(inputs) + + + + + + + + + + + + + inputs.loopTaxAssessmentEligible + + + + + + loopTaxAssessmentEligible + + + + + result + + + + + + + + + + 416 + + + 50 + 307 + 664 + + + 664 + + + 664 + + + 664 + + + 664 + + + 664 + + + 50 + 222 + 383 + + + 383 + + + 383 + + + 383 + + + 50 + 120 + 375 + + + 375 + + + 375 + + + 375 + + + 50 + 204 + 414 + + + 414 + + + 414 + + + 414 + + + 50 + 213 + 445 + + + 445 + + + 445 + + + 445 + + + 50 + 186 + 309 + + + 309 + + + 309 + + + 309 + + + 309 + + + 528 + + + 792 + + + 487 + + + 566 + + + 50 + 251 + 913 + + + 913 + + + 913 + + + 913 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/model-library/dmn/income.dmn b/model-library/dmn/income.dmn new file mode 100644 index 0000000..2ba78ac --- /dev/null +++ b/model-library/dmn/income.dmn @@ -0,0 +1,872 @@ + + + + + + string + + "2023", "2024" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + if inputs.householdSize != null then inputs.householdSize else 0 + + + + + + "2025" + + + + + + if hhSize > 0 then + inputs.inputIncomeLimits[householdSize=hhSize].limit[1] +else + 0 + + + + + + flatten(for person in inputs.people return person.incomes[year=incomeYear]) + + + + + + ["WIA", "Agent Orange", "Loan"] + + + + + + some income in incomesList satisfies income.incomeType in excludedIncomeTypes + + + + + + nn sum(flatten(for income in incomesList return income.grossMonthlyAmount[not(income.incomeType in excludedIncomeTypes)])) + + + + + + nn sum(for person in inputs.people return person.currentMonthlyIncome) + + + + + + if countableIncomesPerPersonAndType != 0 or (countableIncomesPerPersonAndType = 0 and hasExcludedIncomes) then countableIncomesPerPersonAndType else +if currentTotalGrossMonthlyIncomesPerPerson != 0 then currentTotalGrossMonthlyIncomesPerPerson else +inputs.currentMonthlyGrossHouseholdIncome + + + + + + currentMonthlyGrossHouseholdIncome <= incomeLimit + + + + + result + + + + + + + + + + + + + + + + underGrossMonthlyIncomeLimit + + + + + + + + + + + + + if inputs.householdSize != null then inputs.householdSize else 0 + + + + + + "2024" + + + + + + if hhSize > 0 then + inputs.inputIncomeLimits[householdSize=hhSize].limit[1] +else + 0 + + + + + + flatten(for person in inputs.people return person.incomes[year=incomeYear]) + + + + + + [] + + + + + + some income in incomesList satisfies income.incomeType in excludedIncomeTypes + + + + + + nn sum(flatten(for income in incomesList return income.grossAnnualAmount[not(income.incomeType in excludedIncomeTypes)])) + + + + + + nn sum(for person in inputs.people return person.previousYearAnnualIncome) + + + + + + if countableIncomesPerPersonAndType != 0 or (countableIncomesPerPersonAndType = 0 and hasExcludedIncomes) then countableIncomesPerPersonAndType else +if previousYearAnnualIncomePerPerson != 0 then previousYearAnnualIncomePerPerson else +inputs.previousYearGrossHouseholdIncome + + + + + + previousYearGrossHouseholdIncome <= incomeLimit + + + + + result + + + + + + + + + + + + underPreviousAnnualGrossIncomeLimit + + + + + + + + + + + + + +if inputs.people != null and count(inputs.people[primary=true]) > 0 then + inputs.people[primary=true][1] +else + null + + + + + + + if primaryPerson != null then primaryPerson.married else false + + + + + + if primaryPerson != null and count(inputs.people[spouseOfPrimary=true]) > 0 then + inputs.people[spouseOfPrimary=true][1] +else + null + + + + + + if primaryPerson != null and spouseOfPrimary != null then + flatten([primaryPerson.incomes, spouseOfPrimary.incomes]) +else if primaryPerson != null then + primaryPerson.incomes +else + [] + + + + + + "2024" + + + + + + if inputs.inputIncomeLimits = null and count(inputs.inputIncomeLimits[married=primaryMarried?]) = 0 then null + +else inputs.inputIncomeLimits[married=primaryMarried?].limit[1] + + + + + + flatten(for income in primaryAndSpouseIncomes return income[year=incomeYear]) + + + + + + ["test"] + + + + + + some income in incomesList satisfies income.incomeType in excludedIncomeTypes + + + + + + nn sum(flatten(for income in incomesList return income.grossAnnualAmount[not(income.incomeType in excludedIncomeTypes)])) + + + + + + nn sum(primaryPerson.previousYearAnnualIncome, spouseOfPrimary.previousYearAnnualIncome) + + + + + + if countableIncomesPerPersonAndType != 0 or (countableIncomesPerPersonAndType = 0 and hasExcludedIncomes) then countableIncomesPerPersonAndType else +if previousYearAnnualIncomePerPerson != 0 then previousYearAnnualIncomePerPerson else +inputs.previousYearPrimaryAndSpouseGrossIncome + + + + + + if previousYearGrossHouseholdIncome != null and incomeLimit != null then + previousYearGrossHouseholdIncome <= incomeLimit +else + null + + + + + result + + + + + + + + + + + + underMaritalStatusPreviousAnnualGrossIncomeLimit + + + + + + + + + + + + + + +if inputs.people != null and count(inputs.people[primary=true]) > 0 then + inputs.people[primary=true][1] +else + null + + + + + + + if primaryPerson != null then primaryPerson.married else false + + + + + + if primaryPerson != null and count(inputs.people[spouseOfPrimary=true]) > 0 then + inputs.people[spouseOfPrimary=true][1] +else + null + + + + + + if primaryPerson != null and spouseOfPrimary != null then + flatten([primaryPerson.incomes, spouseOfPrimary.incomes]) +else if primaryPerson != null then + primaryPerson.incomes +else + [] + + + + + + "2025" + + + + + + if inputs.inputIncomeLimits = null and count(inputs.inputIncomeLimits[married=primaryMarried?]) = 0 then null + +else inputs.inputIncomeLimits[married=primaryMarried?].limit[1] + + + + + + flatten(for income in primaryAndSpouseIncomes return income[year=incomeYear]) + + + + + + ["test"] + + + + + + some income in incomesList satisfies income.incomeType in excludedIncomeTypes + + + + + + nn sum(flatten(for income in incomesList return income.grossMonthlyAmount[not(income.incomeType in excludedIncomeTypes)])) + + + + + + nn sum(primaryPerson.currentMonthlyIncome, spouseOfPrimary.currentMonthlyIncome) + + + + + + if countableIncomesPerPersonAndType != 0 or (countableIncomesPerPersonAndType = 0 and hasExcludedIncomes) then countableIncomesPerPersonAndType else +if currentMonthlyIncomePerPerson != 0 then currentMonthlyIncomePerPerson else +inputs.currentMonthlyPrimaryAndSpouseGrossIncome + + + + + + if currentMonthlyPrimaryAndSpouseGrossIncome != null and incomeLimit != null then + currentMonthlyPrimaryAndSpouseGrossIncome <= incomeLimit +else + null + + + + + result + + + + + + + + + + + + underMaritalStatusCurrentMonthlyGrossIncomeLimit + + + + Previous annual amt based on Marital Status + + + Current monthly amt based on Marital Status + + + + + + + 50 + 356 + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 311 + + + 50 + 356 + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 311 + + + 50 + 356 + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 430 + + + 50 + 356 + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 1217 + + + 912 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/model-library/dmn/location.dmn b/model-library/dmn/location.dmn new file mode 100644 index 0000000..151f448 --- /dev/null +++ b/model-library/dmn/location.dmn @@ -0,0 +1,479 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + countyFipsLookupDecision != null and list contains(countyFipsLookupDecision, "42101") + + + + + + + + + + + + + + + + + + + + if inputs.countyFips != null then + lookup("countyFips", {countyFips: inputs.countyFips}) +else if inputs.countyName != null and inputs.stateAbbreviation != null then + lookup("countyFips", {countyName: inputs.countyName, stateAbbreviation: inputs.stateAbbreviation}) +else if inputs.zipCode != null then + if inputs.countyName != null then + lookup("countyFips", {zipCode: inputs.zipCode, countyName: inputs.countyName}) + else if inputs.stateAbbreviation != null then + lookup("countyFips", {zipCode: inputs.zipCode, stateAbbreviation: inputs.stateAbbreviation}) + else + lookup("countyFips", {zipCode: inputs.zipCode}) +else + null + + + + + + + + + + + + + + + + if inputs.livesInPhiladelphiaPa != null then + inputs.livesInPhiladelphiaPa +else if countyFipsLookupDecision = null then + null +else if count(countyFipsLookupDecision) = 1 and countyFipsLookupDecision[1] = "42101" then + true +else if mayLiveInPhiladelphiaPa(inputs) then + null +else + false + + + + + + + + + + if stateLookupDecision = null then + null +else + list contains(stateLookupDecision, "PA") + + + + + + + + + + + + + + + + if inputs.stateAbbreviation != null then //locations[stateAbbreviation=inputs.stateAbbreviation].stateAbbreviation + lookup("stateAbbreviation", {stateAbbreviation: inputs.stateAbbreviation}) +else if inputs.countyFips != null then //locations[countyFips=inputs.countyFips].stateAbbreviation + lookup("stateAbbreviation", {countyFips: inputs.countyFips}) +else if inputs.countyName != null and inputs.zipCode != null then //locations[countyName=inputs.countyName and zipCode=inputs.zipCode].stateAbbreviation + lookup("stateAbbreviation", {countyName: inputs.countyName, zipCode: inputs.zipCode}) +else if inputs.zipCode != null then //locations[zipCode=inputs.zipCode].stateAbbreviation + lookup("stateAbbreviation", {zipCode: inputs.zipCode}) +else + null + + + + + + + + + + + + + + + + + +if inputs.livesInPa != null then + inputs.livesInPa +else if stateLookupDecision = null then + null +else if count(stateLookupDecision) = 1 and stateLookupDecision[1] = "PA" then + true +else if mayLiveInPa(inputs) then + null +else + false + + + + + + + + + + + + + "org.acme.functions.LocationService" + + + + + + "lookup(java.lang.String, java.util.Map)" + + + + + + + + + + + 710 + + + 925 + + + 835 + + + 925 + + + 491 + + + 749 + + + 653 + + + 749 + + + + + + + 50 + 100 + 288 + + + 288 + + + 288 + + + 50 + 458 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/model-library/dmn/utility.dmn b/model-library/dmn/utility.dmn new file mode 100644 index 0000000..01b4ff2 --- /dev/null +++ b/model-library/dmn/utility.dmn @@ -0,0 +1,39 @@ + + + BKMs and types that may be useful in any model. + + + Any + + + boolean + + + date + + + number + + + string + + + See Data Types! + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..248ff1a --- /dev/null +++ b/public/index.html @@ -0,0 +1,89 @@ + + + + + + Welcome to Firebase Hosting + + + + + + + + + + + + + + + + + + + +
+

Welcome

+

Firebase Hosting Setup Complete

+

You're seeing this because you've successfully setup Firebase Hosting. Now it's time to go build something extraordinary!

+ Open Hosting Documentation +
+

Firebase SDK Loading…

+ + + + diff --git a/screener-api/.dockerignore b/screener-api/.dockerignore new file mode 100644 index 0000000..b5a077a --- /dev/null +++ b/screener-api/.dockerignore @@ -0,0 +1,6 @@ +* +!target/*-runner +!target/*-runner.jar +!target/lib/* +!target/quarkus-app/* +firebase-config.json \ No newline at end of file diff --git a/screener-api/.gitignore b/screener-api/.gitignore new file mode 100644 index 0000000..5941dde --- /dev/null +++ b/screener-api/.gitignore @@ -0,0 +1,48 @@ +#Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +release.properties +.flattened-pom.xml + +# Eclipse +.project +.classpath +.settings/ +bin/ + +# IntelliJ +.idea +*.ipr +*.iml +*.iws + +# NetBeans +nb-configuration.xml + +# Visual Studio Code +.vscode +.factorypath + +# OSX +.DS_Store + +# Vim +*.swp +*.swo + +# patch +*.orig +*.rej + +# Local environment +.env + +# Plugin directory +/.quarkus/cli/plugins/ +# TLS Certificates +.certs/ + +TODO +config/ \ No newline at end of file diff --git a/screener-api/.mvn/wrapper/.gitignore b/screener-api/.mvn/wrapper/.gitignore new file mode 100644 index 0000000..e72f5e8 --- /dev/null +++ b/screener-api/.mvn/wrapper/.gitignore @@ -0,0 +1 @@ +maven-wrapper.jar diff --git a/screener-api/.mvn/wrapper/MavenWrapperDownloader.java b/screener-api/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..fe7d037 --- /dev/null +++ b/screener-api/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.net.URI; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.concurrent.ThreadLocalRandom; + +public final class MavenWrapperDownloader { + private static final String WRAPPER_VERSION = "3.3.2"; + + private static final boolean VERBOSE = Boolean.parseBoolean(System.getenv("MVNW_VERBOSE")); + + public static void main(String[] args) { + log("Apache Maven Wrapper Downloader " + WRAPPER_VERSION); + + if (args.length != 2) { + System.err.println(" - ERROR wrapperUrl or wrapperJarPath parameter missing"); + System.exit(1); + } + + try { + log(" - Downloader started"); + final URL wrapperUrl = URI.create(args[0]).toURL(); + final String jarPath = args[1].replace("..", ""); // Sanitize path + final Path wrapperJarPath = Paths.get(jarPath).toAbsolutePath().normalize(); + downloadFileFromURL(wrapperUrl, wrapperJarPath); + log("Done"); + } catch (IOException e) { + System.err.println("- Error downloading: " + e.getMessage()); + if (VERBOSE) { + e.printStackTrace(); + } + System.exit(1); + } + } + + private static void downloadFileFromURL(URL wrapperUrl, Path wrapperJarPath) + throws IOException { + log(" - Downloading to: " + wrapperJarPath); + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + final String username = System.getenv("MVNW_USERNAME"); + final char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + Path temp = wrapperJarPath + .getParent() + .resolve(wrapperJarPath.getFileName() + "." + + Long.toUnsignedString(ThreadLocalRandom.current().nextLong()) + ".tmp"); + try (InputStream inStream = wrapperUrl.openStream()) { + Files.copy(inStream, temp, StandardCopyOption.REPLACE_EXISTING); + Files.move(temp, wrapperJarPath, StandardCopyOption.REPLACE_EXISTING); + } finally { + Files.deleteIfExists(temp); + } + log(" - Downloader complete"); + } + + private static void log(String msg) { + if (VERBOSE) { + System.out.println(msg); + } + } + +} diff --git a/screener-api/.mvn/wrapper/maven-wrapper.properties b/screener-api/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..1a580be --- /dev/null +++ b/screener-api/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +wrapperVersion=3.3.2 +distributionType=source +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar \ No newline at end of file diff --git a/screener-api/README.md b/screener-api/README.md new file mode 100644 index 0000000..0f962e1 --- /dev/null +++ b/screener-api/README.md @@ -0,0 +1,66 @@ +# code-with-quarkus + +This project uses Quarkus, the Supersonic Subatomic Java Framework. + +If you want to learn more about Quarkus, please visit its website: . + +## Running the application in dev mode + +You can run your application in dev mode that enables live coding using: + +```shell script +./mvnw quarkus:dev +``` + +> **_NOTE:_** Quarkus now ships with a Dev UI, which is available in dev mode only at . + +## Packaging and running the application + +The application can be packaged using: + +```shell script +./mvnw package +``` + +It produces the `quarkus-run.jar` file in the `target/quarkus-app/` directory. +Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/quarkus-app/lib/` directory. + +The application is now runnable using `java -jar target/quarkus-app/quarkus-run.jar`. + +If you want to build an _über-jar_, execute the following command: + +```shell script +./mvnw package -Dquarkus.package.jar.type=uber-jar +``` + +The application, packaged as an _über-jar_, is now runnable using `java -jar target/*-runner.jar`. + +## Creating a native executable + +You can create a native executable using: + +```shell script +./mvnw package -Dnative +``` + +Or, if you don't have GraalVM installed, you can run the native executable build in a container using: + +```shell script +./mvnw package -Dnative -Dquarkus.native.container-build=true +``` + +You can then execute your native executable with: `./target/code-with-quarkus-1.0.0-SNAPSHOT-runner` + +If you want to learn more about building native executables, please consult . + +## Related Guides + +- REST ([guide](https://quarkus.io/guides/rest)): A Jakarta REST implementation utilizing build time processing and Vert.x. This extension is not compatible with the quarkus-resteasy extension, or any of the extensions that depend on it. + +## Provided Code + +### REST + +Easily start your REST Web Services + +[Related guide section...](https://quarkus.io/guides/getting-started-reactive#reactive-jax-rs-resources) diff --git a/screener-api/mvnw b/screener-api/mvnw new file mode 100755 index 0000000..5e9618c --- /dev/null +++ b/screener-api/mvnw @@ -0,0 +1,332 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.2 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ]; then + + if [ -f /usr/local/etc/mavenrc ]; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ]; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ]; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false +darwin=false +mingw=false +case "$(uname)" in +CYGWIN*) cygwin=true ;; +MINGW*) mingw=true ;; +Darwin*) + darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="$(/usr/libexec/java_home)" + export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home" + export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ]; then + if [ -r /etc/gentoo-release ]; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin; then + [ -n "$JAVA_HOME" ] \ + && JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] \ + && CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw; then + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] \ + && JAVA_HOME="$( + cd "$JAVA_HOME" || ( + echo "cannot cd into $JAVA_HOME." >&2 + exit 1 + ) + pwd + )" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "$javaExecutable" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then + if $darwin; then + javaHome="$(dirname "$javaExecutable")" + javaExecutable="$(cd "$javaHome" && pwd -P)/javac" + else + javaExecutable="$(readlink -f "$javaExecutable")" + fi + javaHome="$(dirname "$javaExecutable")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ]; then + 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 + else + JAVACMD="$( + \unset -f command 2>/dev/null + \command -v java + )" + fi +fi + +if [ ! -x "$JAVACMD" ]; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ]; then + echo "Warning: JAVA_HOME environment variable is not set." >&2 +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ]; then + echo "Path not specified to find_maven_basedir" >&2 + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ]; do + if [ -d "$wdir"/.mvn ]; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$( + cd "$wdir/.." || exit 1 + pwd + ) + fi + # end of workaround + done + printf '%s' "$( + cd "$basedir" || exit 1 + pwd + )" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' <"$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") +if [ -z "$BASE_DIR" ]; then + exit 1 +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" +else + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in wrapperUrl) + wrapperUrl="$safeValue" + break + ;; + esac + done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget >/dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl >/dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in wrapperSha256Sum) + wrapperSha256Sum=$value + break + ;; + esac +done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum >/dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c >/dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c >/dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi +fi + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] \ + && JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] \ + && CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] \ + && MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# shellcheck disable=SC2086 # safe args +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/screener-api/mvnw.cmd b/screener-api/mvnw.cmd new file mode 100644 index 0000000..4136715 --- /dev/null +++ b/screener-api/mvnw.cmd @@ -0,0 +1,206 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.2 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. >&2 +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. >&2 +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. >&2 +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. >&2 +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash;"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Error 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Error 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Error 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/screener-api/pom.xml b/screener-api/pom.xml new file mode 100644 index 0000000..5eaf66c --- /dev/null +++ b/screener-api/pom.xml @@ -0,0 +1,169 @@ + + + 4.0.0 + org.acme + screener-api + 1.0.0-SNAPSHOT + + + 3.14.0 + 21 + UTF-8 + UTF-8 + quarkus-bom + io.quarkus.platform + 3.22.2 + true + 3.5.2 + + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + + + + + io.quarkus + quarkus-rest + + + io.quarkus + quarkus-arc + + + io.quarkiverse.qute.web + quarkus-qute-web + + + io.quarkus + quarkus-rest-jackson + + + com.fasterxml.jackson.core + jackson-databind + 2.19.0 + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + org.kie + kie-dmn-core + 10.0.0 + + + org.drools + drools-xml-support + 10.0.0 + + + com.google.firebase + firebase-admin + 9.4.3 + + + + + + org.xerial + sqlite-jdbc + 3.40.1.0 + + + io.quarkiverse.jdbc + quarkus-jdbc-sqlite + 3.0.0 + + + io.quarkus + quarkus-agroal + + + + + + + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus.platform.version} + true + + + + build + generate-code + generate-code-tests + native-image-agent + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + true + + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native + + + native + + + + false + true + + + + diff --git a/screener-api/src/main/docker/Dockerfile.jvm b/screener-api/src/main/docker/Dockerfile.jvm new file mode 100644 index 0000000..469e20d --- /dev/null +++ b/screener-api/src/main/docker/Dockerfile.jvm @@ -0,0 +1,102 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package -DskipTests +# +# Then, build the image with: +# +# podman build -f src/main/docker/Dockerfile.jvm -t us-central1-docker.pkg.dev/benefits-decision-toolkit/benefit-decision-toolkit/screener-api:latest . +# +# push to google artifact registry +# +# podman push us-central1-docker.pkg.dev/benefits-decision-toolkit/benefit-decision-toolkit/screener-api:latest +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 us-central1-docker.pkg.dev/benefits-decision-toolkit/benefit-decision-toolkit/screener-api:latest +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. +# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 +# when running the container +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 quarkus/code-with-quarkus-jvm +# +# This image uses the `run-java.sh` script to run the application. +# This scripts computes the command line to execute your Java application, and +# includes memory/GC tuning. +# You can configure the behavior using the following environment properties: +# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") - Be aware that this will override +# the default JVM options, use `JAVA_OPTS_APPEND` to append options +# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options +# in JAVA_OPTS (example: "-Dsome.property=foo") +# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is +# used to calculate a default maximal heap memory based on a containers restriction. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio +# of the container available memory as set here. The default is `50` which means 50% +# of the available memory is used as an upper boundary. You can skip this mechanism by +# setting this value to `0` in which case no `-Xmx` option is added. +# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This +# is used to calculate a default initial heap memory based on the maximum heap memory. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio +# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` +# is used as the initial heap size. You can skip this mechanism by setting this value +# to `0` in which case no `-Xms` option is added (example: "25") +# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. +# This is used to calculate the maximum value of the initial heap memory. If used in +# a container without any memory constraints for the container then this option has +# no effect. If there is a memory constraint then `-Xms` is limited to the value set +# here. The default is 4096MB which means the calculated value of `-Xms` never will +# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") +# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output +# when things are happening. This option, if set to true, will set +# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). +# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: +# true"). +# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). +# - CONTAINER_CORE_LIMIT: A calculated core limit as described in +# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") +# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). +# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. +# (example: "20") +# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. +# (example: "40") +# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. +# (example: "4") +# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus +# previous GC times. (example: "90") +# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") +# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") +# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should +# contain the necessary JRE command-line options to specify the required GC, which +# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). +# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") +# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") +# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be +# accessed directly. (example: "foo.example.com,bar.example.com") +# +### +FROM registry.access.redhat.com/ubi9/openjdk-21:1.21 + +ENV LANGUAGE='en_US:en' + + +# We make four distinct layers so if there are application changes the library layers can be re-used +COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/ +COPY --chown=185 target/quarkus-app/*.jar /deployments/ +COPY --chown=185 target/quarkus-app/app/ /deployments/app/ +COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/ + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] + diff --git a/screener-api/src/main/docker/Dockerfile.legacy-jar b/screener-api/src/main/docker/Dockerfile.legacy-jar new file mode 100644 index 0000000..7054d37 --- /dev/null +++ b/screener-api/src/main/docker/Dockerfile.legacy-jar @@ -0,0 +1,94 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package -Dquarkus.package.jar.type=legacy-jar +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/code-with-quarkus-legacy-jar . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/code-with-quarkus-legacy-jar +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. +# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 +# when running the container +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 quarkus/code-with-quarkus-legacy-jar +# +# This image uses the `run-java.sh` script to run the application. +# This scripts computes the command line to execute your Java application, and +# includes memory/GC tuning. +# You can configure the behavior using the following environment properties: +# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") - Be aware that this will override +# the default JVM options, use `JAVA_OPTS_APPEND` to append options +# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options +# in JAVA_OPTS (example: "-Dsome.property=foo") +# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is +# used to calculate a default maximal heap memory based on a containers restriction. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio +# of the container available memory as set here. The default is `50` which means 50% +# of the available memory is used as an upper boundary. You can skip this mechanism by +# setting this value to `0` in which case no `-Xmx` option is added. +# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This +# is used to calculate a default initial heap memory based on the maximum heap memory. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio +# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` +# is used as the initial heap size. You can skip this mechanism by setting this value +# to `0` in which case no `-Xms` option is added (example: "25") +# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. +# This is used to calculate the maximum value of the initial heap memory. If used in +# a container without any memory constraints for the container then this option has +# no effect. If there is a memory constraint then `-Xms` is limited to the value set +# here. The default is 4096MB which means the calculated value of `-Xms` never will +# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") +# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output +# when things are happening. This option, if set to true, will set +# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). +# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: +# true"). +# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). +# - CONTAINER_CORE_LIMIT: A calculated core limit as described in +# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") +# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). +# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. +# (example: "20") +# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. +# (example: "40") +# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. +# (example: "4") +# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus +# previous GC times. (example: "90") +# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") +# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") +# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should +# contain the necessary JRE command-line options to specify the required GC, which +# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). +# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") +# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") +# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be +# accessed directly. (example: "foo.example.com,bar.example.com") +# +### +FROM registry.access.redhat.com/ubi9/openjdk-21:1.21 + +ENV LANGUAGE='en_US:en' + + +COPY target/lib/* /deployments/lib/ +COPY target/*-runner.jar /deployments/quarkus-run.jar + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] diff --git a/screener-api/src/main/docker/Dockerfile.native b/screener-api/src/main/docker/Dockerfile.native new file mode 100644 index 0000000..8804f76 --- /dev/null +++ b/screener-api/src/main/docker/Dockerfile.native @@ -0,0 +1,29 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. +# +# Before building the container image run: +# +# ./mvnw package -Dnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native -t gcr.io/benefits-decision-toolkit/screener-api . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/screener-api +# +# The ` registry.access.redhat.com/ubi8/ubi-minimal:8.10` base image is based on UBI 9. +# To use UBI 8, switch to `quay.io/ubi8/ubi-minimal:8.10`. +### +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.10 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root --chmod=0755 target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/screener-api/src/main/docker/Dockerfile.native-micro b/screener-api/src/main/docker/Dockerfile.native-micro new file mode 100644 index 0000000..c883366 --- /dev/null +++ b/screener-api/src/main/docker/Dockerfile.native-micro @@ -0,0 +1,32 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. +# It uses a micro base image, tuned for Quarkus native executables. +# It reduces the size of the resulting container image. +# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. +# +# Before building the container image run: +# +# ./mvnw package -DskipTests -Dnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native-micro -t gcr.io/benefits-decision-toolkit/screener-api . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 gcr.io/benefits-decision-toolkit/screener-api +# +# The `quay.io/quarkus/quarkus-micro-image:2.0` base image is based on UBI 9. +# To use UBI 8, switch to `quay.io/quarkus/quarkus-micro-image:2.0`. +### +FROM quay.io/quarkus/quarkus-micro-image:2.0 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root --chmod=0755 target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/screener-api/src/main/java/org/acme/constants/CollectionNames.java b/screener-api/src/main/java/org/acme/constants/CollectionNames.java new file mode 100644 index 0000000..8c6106b --- /dev/null +++ b/screener-api/src/main/java/org/acme/constants/CollectionNames.java @@ -0,0 +1,5 @@ +package org.acme.constants; + +public class CollectionNames { + public static final String SCREENER_COLLECTION = "screener"; +} diff --git a/screener-api/src/main/java/org/acme/constants/FieldNames.java b/screener-api/src/main/java/org/acme/constants/FieldNames.java new file mode 100644 index 0000000..6792de8 --- /dev/null +++ b/screener-api/src/main/java/org/acme/constants/FieldNames.java @@ -0,0 +1,17 @@ +package org.acme.constants; + + +public class FieldNames { + public static final String ID = "id"; + public static final String WORKING_FORM_PATH = "working_form_path"; + public static final String SCREENER_NAME = "screener_name"; + public static final String IS_PUBLISHED = "is_published"; + public static final String WORKING_DMN_PATH = "working_dmn_path"; + public static final String OWNER_ID = "owner_id"; + public static final String LAST_PUBLISHED_DATE = "last_published_date"; + public static final String ORGANIZATION_NAME = "organization_name"; + public static final String PUBLISHED_DMN_NAME = "published_dmn_name"; + public static final String PUBLISHED_DMN_NAMESPACE = "published_dmn_namespace"; + // Add other field names here + +} \ No newline at end of file diff --git a/screener-api/src/main/java/org/acme/controller/DecisionResource.java b/screener-api/src/main/java/org/acme/controller/DecisionResource.java new file mode 100644 index 0000000..59fb3a3 --- /dev/null +++ b/screener-api/src/main/java/org/acme/controller/DecisionResource.java @@ -0,0 +1,65 @@ +package org.acme.controller; + +import jakarta.inject.Inject; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.acme.model.Screener; +import org.acme.repository.ScreenerRepository; +import org.acme.service.DmnService; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; + +@Path("/api/decision") +public class DecisionResource { + + @Inject + DmnService dmnService; + + @Inject + ScreenerRepository screenerRepository; + + @POST + @Consumes(MediaType.APPLICATION_JSON) + public Response post(@QueryParam("screenerId") String screenerId, Map inputData) { + try{ + if (screenerId == null || screenerId.isBlank()){ + return Response.status(Response.Status.BAD_REQUEST) + .entity("Error: Missing required query parameter: screenerId") + .build(); + } + + if (inputData == null || inputData.isEmpty()){ + return Response.status(Response.Status.BAD_REQUEST) + .entity("Error: Missing decision inputs") + .build(); + } + + + Optional screenerOptional = screenerRepository.getScreener(screenerId); + + String notFoundResponseMessage = String.format("Form %s was not found", screenerId); + if (screenerOptional.isEmpty()){ + throw new NotFoundException(notFoundResponseMessage); + } + + Screener screener = screenerOptional.get(); + if (screener.getFormSchema() == null || !screener.isPublished()) { + throw new NotFoundException(notFoundResponseMessage); + } + + Map result = dmnService.evaluateDecision(screener, inputData); + + if (!result.isEmpty()){ + return Response.ok(Collections.emptyList()).entity(result).build(); + } + + else { + return Response.ok(Collections.emptyList()).build(); + }} + catch (Exception e){ + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + } +} \ No newline at end of file diff --git a/screener-api/src/main/java/org/acme/controller/ScreenerResource.java b/screener-api/src/main/java/org/acme/controller/ScreenerResource.java new file mode 100644 index 0000000..603618b --- /dev/null +++ b/screener-api/src/main/java/org/acme/controller/ScreenerResource.java @@ -0,0 +1,37 @@ +package org.acme.controller; + +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.inject.Inject; + +import jakarta.ws.rs.core.Response; +import org.acme.model.Screener; +import org.acme.repository.ScreenerRepository; + +import java.util.Optional; + +@Path("/api/screener/{screenerId}") +public class ScreenerResource { + + @Inject + ScreenerRepository screenerRepository; + + @GET + @Produces(MediaType.APPLICATION_JSON) + public Response get(@PathParam("screenerId") String screenerId) { + + Optional screenerOptional = screenerRepository.getScreener(screenerId); + + String notFoundResponseMessage = String.format("Form %s was not found", screenerId); + if (screenerOptional.isEmpty()){ + throw new NotFoundException(notFoundResponseMessage); + } + + Screener screener = screenerOptional.get(); + if (screener.getFormSchema() == null || !screener.isPublished()) { + throw new NotFoundException(notFoundResponseMessage); + } + + return Response.ok(screener, MediaType.APPLICATION_JSON).build(); + } +} \ No newline at end of file diff --git a/screener-api/src/main/java/org/acme/functions/LocationService.java b/screener-api/src/main/java/org/acme/functions/LocationService.java new file mode 100644 index 0000000..13f8c78 --- /dev/null +++ b/screener-api/src/main/java/org/acme/functions/LocationService.java @@ -0,0 +1,86 @@ +package org.acme.functions; + +import io.agroal.api.AgroalDataSource; +import io.quarkus.arc.Arc; +import io.quarkus.arc.Unremovable; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Unremovable +@ApplicationScoped +public class LocationService { + + @Inject + AgroalDataSource dataSource; + + public Connection getDbConnection() throws SQLException { + return dataSource.getConnection(); + } + + public static List lookup(String column, Map filters) { + List results = new ArrayList<>(); + StringBuilder sql = new StringBuilder("SELECT DISTINCT ").append(column).append(" FROM locations WHERE "); + + // construct WHERE clause; assume everything is ANDed together + boolean first = true; + for (String key : filters.keySet()) { + if (!first) { + sql.append(" AND "); + } + sql.append(key).append(" = ?"); + first = false; + } + + LocationService service = Arc.container().instance(LocationService.class).get(); + + Connection connection = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + + try { + connection = service.getDbConnection(); + pstmt = connection.prepareStatement(sql.toString()); + + // Set the values dynamically + int index = 1; + for (Object value : filters.values()) { + String stringValue = value.toString(); // db only has strings + pstmt.setString(index++, stringValue); + } + + rs = pstmt.executeQuery(); + + while(rs.next()) { + String thisString = rs.getString(column); + results.add(thisString); + } + } catch (SQLException e) { + e.printStackTrace(); + } finally { + try { + if (rs != null) { + rs.close(); + } + if (pstmt != null) { + pstmt.close(); + } + if (connection != null) { + connection.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + + return results; + + } +} diff --git a/screener-api/src/main/java/org/acme/infrastructure/FirebaseInitializer.java b/screener-api/src/main/java/org/acme/infrastructure/FirebaseInitializer.java new file mode 100644 index 0000000..48edd55 --- /dev/null +++ b/screener-api/src/main/java/org/acme/infrastructure/FirebaseInitializer.java @@ -0,0 +1,38 @@ +package org.acme.infrastructure; + +import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseOptions; +import com.google.auth.oauth2.GoogleCredentials; +import io.quarkus.runtime.Startup; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Singleton; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import jakarta.annotation.PostConstruct; +import java.io.FileInputStream; +import java.io.IOException; + +@Startup +@ApplicationScoped +public class FirebaseInitializer { + + @PostConstruct + void init() { + + if (FirebaseApp.getApps().isEmpty()) { + try { + + FirebaseOptions options = new FirebaseOptions.Builder() + .setCredentials(GoogleCredentials.getApplicationDefault()) + .setProjectId("benefits-decision-toolkit") + .setStorageBucket("benefits-decision-toolkit.firebasestorage.app") + .build(); + + FirebaseApp.initializeApp(options); + System.out.println("✅ Firebase initialized"); + } catch (IOException e) { + throw new RuntimeException("Failed to initialize Firebase", e); + } + } + } +} \ No newline at end of file diff --git a/screener-api/src/main/java/org/acme/mapper/ScreenerMapper.java b/screener-api/src/main/java/org/acme/mapper/ScreenerMapper.java new file mode 100644 index 0000000..0a6af19 --- /dev/null +++ b/screener-api/src/main/java/org/acme/mapper/ScreenerMapper.java @@ -0,0 +1,36 @@ +package org.acme.mapper; + +import org.acme.constants.FieldNames; +import org.acme.model.Screener; + +import java.util.HashMap; +import java.util.Map; + +public class ScreenerMapper { + + public static Screener fromMap(Map map){ + Screener screener = new Screener(); + + if (doesAttributeExistOfType(map, FieldNames.ID, String.class)){ + screener.setId((String) map.get(FieldNames.ID)); + } + if (doesAttributeExistOfType(map, FieldNames.SCREENER_NAME, String.class)){ + screener.setScreenerName((String) map.get(FieldNames.SCREENER_NAME)); + } + if (doesAttributeExistOfType(map, FieldNames.IS_PUBLISHED, Boolean.class)){ + screener.setIsPublished((Boolean) map.get(FieldNames.IS_PUBLISHED)); + } + if (doesAttributeExistOfType(map, FieldNames.PUBLISHED_DMN_NAME, String.class)){ + screener.setPublishedDmnName((String) map.get(FieldNames.PUBLISHED_DMN_NAME)); + } + if (doesAttributeExistOfType(map, FieldNames.PUBLISHED_DMN_NAMESPACE, String.class)){ + screener.setPublishedDmnNameSpace((String) map.get(FieldNames.PUBLISHED_DMN_NAMESPACE)); + } + + return screener; + } + + private static boolean doesAttributeExistOfType(Map map, String attributeName, Class expectedType) { + return map.containsKey(attributeName) && expectedType.isInstance(map.get(attributeName)); + } +} diff --git a/screener-api/src/main/java/org/acme/model/Screener.java b/screener-api/src/main/java/org/acme/model/Screener.java new file mode 100644 index 0000000..30f0a22 --- /dev/null +++ b/screener-api/src/main/java/org/acme/model/Screener.java @@ -0,0 +1,77 @@ +package org.acme.model; + + +import java.util.HashMap; +import java.util.Map; + +public class Screener { + private String id; + private Map formSchema; + private Boolean isPublished; + private String organizationName; + private String screenerName; + private String publishedDmnNameSpace; + private String publishedDmnName; + + public Screener(Map model, boolean isPublished){ + this.formSchema = model; + this.isPublished = isPublished; + } + + public Screener(){ + this.formSchema = new HashMap<>(); + this.isPublished = false; + } + + public void setId(String id){ + this.id = id; + } + + public String getId(){ + return this.id; + } + + public String getScreenerName(){ + return this.screenerName; + } + + public void setScreenerName(String screenerName){ + this.screenerName = screenerName; + } + + public Map getFormSchema() { + return formSchema; + } + + public Boolean isPublished() { + return isPublished; + } + + public void setIsPublished(Boolean isPublished){ + this.isPublished = isPublished; + } + + public void setFormSchema(Map formSchema) { + this.formSchema = formSchema; + } + + public String getOrganizationName(){ + return this.organizationName; + } + + public void setOrganizationName(){ + this.organizationName = organizationName; + } + public String getPublishedDmnNameSpace(){ + return this.publishedDmnNameSpace; + } + public String getPublishedDmnName(){ + return this.publishedDmnName; + } + public void setPublishedDmnName(String name){ + this.publishedDmnName = name; + } + public void setPublishedDmnNameSpace(String nameSpace){ + this.publishedDmnNameSpace = nameSpace; + } +} diff --git a/screener-api/src/main/java/org/acme/repository/ScreenerRepository.java b/screener-api/src/main/java/org/acme/repository/ScreenerRepository.java new file mode 100644 index 0000000..fbb1864 --- /dev/null +++ b/screener-api/src/main/java/org/acme/repository/ScreenerRepository.java @@ -0,0 +1,10 @@ +package org.acme.repository; + +import org.acme.model.Screener; + +import java.io.InputStream; +import java.util.Optional; + +public interface ScreenerRepository { + public Optional getScreener(String screenerId); +} diff --git a/screener-api/src/main/java/org/acme/repository/ScreenerRepositoryImpl.java b/screener-api/src/main/java/org/acme/repository/ScreenerRepositoryImpl.java new file mode 100644 index 0000000..cdfd18a --- /dev/null +++ b/screener-api/src/main/java/org/acme/repository/ScreenerRepositoryImpl.java @@ -0,0 +1,36 @@ +package org.acme.repository; + +import io.quarkus.logging.Log; +import jakarta.enterprise.context.ApplicationScoped; +import org.acme.constants.CollectionNames; +import org.acme.mapper.ScreenerMapper; +import org.acme.model.Screener; + +import org.acme.repository.utils.FirestoreUtils; +import org.acme.repository.utils.StorageUtils; + +import java.util.Map; +import java.util.Optional; + +@ApplicationScoped +public class ScreenerRepositoryImpl implements ScreenerRepository { + + @Override + public Optional getScreener(String screenerId) { + Optional> dataOpt = FirestoreUtils.getFirestoreDocById(CollectionNames.SCREENER_COLLECTION, screenerId); + + if (dataOpt.isEmpty()){ + return Optional.empty(); + } + + Map data = dataOpt.get(); + Screener screener = ScreenerMapper.fromMap(data); + + String formPath = StorageUtils.getScreenerPublishedFormSchemaPath(screenerId); + Map formSchema = StorageUtils.getFormSchemaFromStorage(formPath); + screener.setFormSchema(formSchema); + + return Optional.of(screener); + } +} + diff --git a/screener-api/src/main/java/org/acme/repository/utils/FirestoreUtils.java b/screener-api/src/main/java/org/acme/repository/utils/FirestoreUtils.java new file mode 100644 index 0000000..30aeecc --- /dev/null +++ b/screener-api/src/main/java/org/acme/repository/utils/FirestoreUtils.java @@ -0,0 +1,48 @@ +package org.acme.repository.utils; + +import com.google.api.core.ApiFuture; +import com.google.cloud.firestore.DocumentSnapshot; +import com.google.cloud.firestore.Firestore; +import com.google.cloud.firestore.QueryDocumentSnapshot; +import com.google.cloud.firestore.QuerySnapshot; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.Bucket; +import com.google.firebase.cloud.FirestoreClient; +import com.google.firebase.cloud.StorageClient; +import io.quarkus.logging.Log; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class FirestoreUtils { + + + public static Optional> getFirestoreDocById(String collection, String id) { + + Firestore db = FirestoreClient.getFirestore(); + try { + + DocumentSnapshot doc = db.collection(collection) + .document(id) + .get().get(); + + + if (!doc.exists()) { + return Optional.empty(); + } + + + Map data = doc.getData(); + data.put("id", doc.getId()); + + return Optional.of(data); + + }catch(Exception e){ + Log.error("Error fetching document from firestore: ", e); + return Optional.empty(); + } + } +} diff --git a/screener-api/src/main/java/org/acme/repository/utils/StorageUtils.java b/screener-api/src/main/java/org/acme/repository/utils/StorageUtils.java new file mode 100644 index 0000000..2bf9b26 --- /dev/null +++ b/screener-api/src/main/java/org/acme/repository/utils/StorageUtils.java @@ -0,0 +1,89 @@ +package org.acme.repository.utils; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.Bucket; +import com.google.firebase.cloud.StorageClient; +import io.quarkus.logging.Log; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.Map; +import java.util.Optional; + +public class StorageUtils { + + public static String getScreenerPublishedFormSchemaPath(String screenerId){ + return "form/published/" + screenerId + ".json"; + } + + public static String getScreenerPublishedDmnModelPath(String screenerId){ + return "dmn/published/" + screenerId + ".dmn"; + } + + + public static String getPublishedCompiledDmnModelPath(String screenerId){ + return "compiled_dmn_models/published/" + screenerId + "/kiebase.ser"; + } + + public static Map getFormSchemaFromStorage(String filePath) { + try { + Bucket bucket = StorageClient.getInstance().bucket(); + Blob blob = bucket.get(filePath); + + if (blob == null || !blob.exists()) { + return null; + } + + byte[] content = blob.getContent(); + ObjectMapper mapper = new ObjectMapper(); + Map formSchema = mapper.readValue(new ByteArrayInputStream(content), new TypeReference>() { + }); + + return formSchema; + + } catch (Exception e){ + Log.error("Error fetching form model from firebase storage: ", e); + return null; + } + } + + public static Optional getFileBytesFromStorage(String filePath) { + try { + Bucket bucket = StorageClient.getInstance().bucket(); + Blob blob = bucket.get(filePath); + + if (blob == null || !blob.exists()) { + return Optional.empty(); + } + + byte[] data = blob.getContent(); + + return Optional.of(data); + + } catch (Exception e){ + Log.error("Error fetching file from firebase storage: ", e); + return Optional.empty(); + } + } + + public static Optional getFileInputStreamFromStorage(String filePath) { + try { + Bucket bucket = StorageClient.getInstance().bucket(); + Blob blob = bucket.get(filePath); + + if (blob == null || !blob.exists()) { + return Optional.empty(); + } + + byte[] data = blob.getContent(); + + return Optional.of(new ByteArrayInputStream(data)); + + } catch (Exception e){ + Log.error("Error fetching file from firebase storage: ", e); + return Optional.empty(); + } + } +} diff --git a/screener-api/src/main/java/org/acme/service/DmnParser.java b/screener-api/src/main/java/org/acme/service/DmnParser.java new file mode 100644 index 0000000..8a0da74 --- /dev/null +++ b/screener-api/src/main/java/org/acme/service/DmnParser.java @@ -0,0 +1,45 @@ +package org.acme.service; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.ByteArrayInputStream; + +public class DmnParser { + private String nameSpace; + private String name; + + public DmnParser(String dmnXml) { + + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(new ByteArrayInputStream(dmnXml.getBytes("UTF-8"))); + + // Get the document element (dmn:definitions) + Element root = doc.getDocumentElement(); + + // Extract attributes + String name = root.getAttribute("name"); + String namespace = root.getAttribute("namespace"); + + this.name = name; + this.nameSpace = namespace; + System.out.println("Name: " + name); + System.out.println("Namespace: " + namespace); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + public String getName() { + return name; + } + public String getNameSpace(){ + return nameSpace; + } +} diff --git a/screener-api/src/main/java/org/acme/service/DmnService.java b/screener-api/src/main/java/org/acme/service/DmnService.java new file mode 100644 index 0000000..fecbe55 --- /dev/null +++ b/screener-api/src/main/java/org/acme/service/DmnService.java @@ -0,0 +1,12 @@ +package org.acme.service; + +import org.acme.model.Screener; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Map; + +public interface DmnService { + public Map evaluateDecision(Screener screener, Map inputs) throws IOException; +} diff --git a/screener-api/src/main/java/org/acme/service/KieDmnService.java b/screener-api/src/main/java/org/acme/service/KieDmnService.java new file mode 100644 index 0000000..fa05779 --- /dev/null +++ b/screener-api/src/main/java/org/acme/service/KieDmnService.java @@ -0,0 +1,80 @@ +package org.acme.service; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.ws.rs.NotFoundException; +import org.acme.model.Screener; +import org.acme.repository.utils.StorageUtils; +import org.kie.api.KieServices; +import org.kie.api.builder.KieModule; +import org.kie.api.builder.ReleaseId; +import org.kie.api.io.Resource; +import org.kie.api.runtime.KieContainer; +import org.kie.api.runtime.KieSession; +import org.kie.dmn.api.core.*; +import java.io.*; +import java.util.*; + +@ApplicationScoped +public class KieDmnService implements DmnService { + public Map evaluateDecision(Screener screener, Map inputs) throws IOException { + + String filePath = StorageUtils.getPublishedCompiledDmnModelPath(screener.getId()); + Optional dmnDataOpt = StorageUtils.getFileBytesFromStorage(filePath); + + + if (dmnDataOpt.isEmpty()){ + throw new NotFoundException(); + } + + byte[] dmnModuleData = dmnDataOpt.get(); + + KieSession kieSession = initializeKieSession(dmnModuleData); + DMNRuntime dmnRuntime = kieSession.getKieRuntime(DMNRuntime.class); + + try { + DMNModel dmnModel = dmnRuntime.getModel(screener.getPublishedDmnNameSpace(), screener.getPublishedDmnName()); + + DMNContext context = dmnRuntime.newContext(); + for (String key : inputs.keySet()) { + context.set(key, inputs.get(key)); + } + + DMNResult dmnResult = dmnRuntime.evaluateAll(dmnModel, context); + + Map response = new LinkedHashMap<>(); + response.put("inputs", inputs); + + + List> decisions = new ArrayList<>(); + for (DMNDecisionResult decisionResult : dmnResult.getDecisionResults()) { + Map decisionDetail = new LinkedHashMap<>(); + decisionDetail.put("decisionName", decisionResult.getDecisionName()); + decisionDetail.put("result", decisionResult.getResult()); + decisionDetail.put("status", decisionResult.getEvaluationStatus().toString()); + decisions.add(decisionDetail); + } + + response.put("decisions", decisions); + + kieSession.dispose(); + return response; + } + catch (Exception e){ + return new HashMap<>(); + } finally{ + if (kieSession != null) { + kieSession.dispose(); + } + } + } + + private KieSession initializeKieSession(byte[] moduleBytes) throws IOException { + KieServices kieServices = KieServices.Factory.get(); + Resource jarResource = kieServices.getResources().newByteArrayResource(moduleBytes); + KieModule kieModule = kieServices.getRepository().addKieModule(jarResource); + + ReleaseId releaseId = kieModule.getReleaseId(); + KieContainer kieContainer = kieServices.newKieContainer(releaseId); + return kieContainer.newKieSession(); + } +} diff --git a/screener-api/src/main/resources/application.properties b/screener-api/src/main/resources/application.properties new file mode 100644 index 0000000..ad8f8a4 --- /dev/null +++ b/screener-api/src/main/resources/application.properties @@ -0,0 +1,8 @@ +quarkus.http.cors.enabled=true +quarkus.http.cors.origins=* +quarkus.http.cors.methods=GET,POST +quarkus.http.cors.headers=Content-Type + +quarkus.datasource.db-kind=sqlite +quarkus.datasource.jdbc.url=jdbc:sqlite::resource:data/locations.db +quarkus.datasource.jdbc.driver=org.sqlite.JDBC diff --git a/screener-api/src/main/resources/data/locations.db b/screener-api/src/main/resources/data/locations.db new file mode 100644 index 0000000..ccfb0bd Binary files /dev/null and b/screener-api/src/main/resources/data/locations.db differ diff --git a/screener-api/src/main/resources/dish.dmn b/screener-api/src/main/resources/dish.dmn new file mode 100644 index 0000000..402eaba --- /dev/null +++ b/screener-api/src/main/resources/dish.dmn @@ -0,0 +1,68 @@ + + + + + + season + + + + guestCount + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Save money + + + [5..8] + + + + + + Less effort + + + 8]]> + + + + + + Hey, why not!? + + + + + + + + + + diff --git a/screener-api/src/main/resources/dish.json b/screener-api/src/main/resources/dish.json new file mode 100644 index 0000000..a5cdf54 --- /dev/null +++ b/screener-api/src/main/resources/dish.json @@ -0,0 +1,58 @@ +{ + "components": [ + { + "text": "What Dish To Make?", + "type": "text", + "layout": { + "row": "Row_0i252b2", + "columns": null + }, + "id": "Field_1ejqxvr" + }, + { + "label": "What season is it?", + "values": [ + { + "label": "Spring", + "value": "Spring" + }, + { + "label": "Summer", + "value": "Summer" + }, + { + "label": "Fall", + "value": "Fall" + }, + { + "label": "Winter", + "value": "Winter" + } + ], + "type": "select", + "layout": { + "row": "Row_14pnbj9", + "columns": null + }, + "id": "Field_1violuj", + "key": "season" + }, + { + "label": "How many guests?", + "type": "number", + "layout": { + "row": "Row_0etabra", + "columns": null + }, + "id": "Field_03n445p", + "key": "guestCount" + } + ], + "type": "default", + "id": "Form_1df4wus", + "exporter": { + "name": "form-js (https://demo.bpmn.io)", + "version": "1.15.0" + }, + "schemaVersion": 18 +} \ No newline at end of file diff --git a/screener-api/src/main/resources/form.json b/screener-api/src/main/resources/form.json new file mode 100644 index 0000000..4480bb1 --- /dev/null +++ b/screener-api/src/main/resources/form.json @@ -0,0 +1,52 @@ +{ + "components": [ + { + "text": "TEST", + "type": "text", + "layout": { + "row": "Row_0duwi1b", + "columns": null + }, + "id": "Field_1oplq09" + }, + { + "label": "Checkbox group", + "values": [ + { + "label": "Value", + "value": "value" + } + ], + "type": "checklist", + "layout": { + "row": "Row_1w144hj", + "columns": null + }, + "id": "Field_1xe7ivy", + "key": "checklist_un5s7e" + }, + { + "label": "Select", + "values": [ + { + "label": "Value", + "value": "value" + } + ], + "type": "select", + "layout": { + "row": "Row_0p34xzv", + "columns": null + }, + "id": "Field_04q9ave", + "key": "select_eerz64" + } + ], + "type": "default", + "id": "Form_0wc0jzr", + "exporter": { + "name": "form-js (https://demo.bpmn.io)", + "version": "1.15.0" + }, + "schemaVersion": 18 +} \ No newline at end of file diff --git a/screener-api/src/main/resources/phlPropertyTaxRelief.json b/screener-api/src/main/resources/phlPropertyTaxRelief.json new file mode 100644 index 0000000..4bcb93c --- /dev/null +++ b/screener-api/src/main/resources/phlPropertyTaxRelief.json @@ -0,0 +1,508 @@ +{ + "components": [ + { + "label": "Philly Property Tax Relief Screener", + "components": [ + { + "text": "_Fill out the form below to see if you're eligible for one or more tax relief benefits._", + "label": "Text view", + "type": "text", + "layout": { + "row": "Row_1wo3mpk", + "columns": null + }, + "id": "Field_0923rjq" + }, + { + "label": "Do you live in Philadelphia?", + "type": "radio", + "layout": { + "row": "Row_12t2adl", + "columns": null + }, + "id": "Field_0zyojbc", + "key": "inputs.livesInPhiladelphiaPa", + "valuesExpression": "=[\n {label: \"Yes\", value: true},\n {label: \"No\", value: false}\n]", + "validate": { + "required": true + } + }, + { + "label": "Are you already in enrolled in any of the following programs?", + "values": [ + { + "label": "Homestead Exemption", + "value": "phlHomesteadExemption" + }, + { + "label": "Owner-Occupied Payment Agreement (OOPA)", + "value": "phlOopa" + }, + { + "label": "Longtime Owner Occupant Program (LOOP)", + "value": "phlLoop" + }, + { + "label": "Low-Income Tax Freeze", + "value": "phlLowIncomeTaxFreeze" + }, + { + "label": "Senior Citizen Tax Freeze", + "value": "phlSeniorCitizenTaxFreeze" + }, + { + "label": "I'm not enrolled in any of the above programs", + "value": "none" + } + ], + "type": "checklist", + "layout": { + "row": "Row_07hekck", + "columns": null + }, + "id": "Field_11h3faf", + "key": "inputs.enrollments", + "conditional": { + "hide": "=list contains([null, false], inputs.livesInPhiladelphiaPa)" + }, + "description": "(check all that apply)", + "validate": { + "required": true + }, + "readonly": false, + "disabled": false + }, + { + "label": "Are you a homeowner?", + "type": "radio", + "layout": { + "row": "Row_1d02yrp", + "columns": null + }, + "id": "Field_1rfkgwt", + "key": "inputs.homeowner", + "valuesExpression": "=[\n {label: \"Yes\", value: true},\n {label: \"No\", value: false}\n]", + "validate": { + "required": true + }, + "conditional": { + "hide": "=(inputs.enrollments = null) or (inputs.enrollments = [])" + } + }, + { + "label": "Do you have equitable interest in the home you live in?", + "type": "radio", + "layout": { + "row": "Row_13fd6tp", + "columns": null + }, + "id": "Field_0ca5fan", + "key": "inputs.equitableInterestInProperty", + "valuesExpression": "=[\n {label: \"Yes\", value: true},\n {label: \"No\", value: false}\n]", + "conditional": { + "hide": "=list contains([null, true], inputs.homeowner)" + }, + "validate": { + "required": true + } + }, + { + "text": "_An equitable interest may include:_\n_- You inherited the house from a deceased relative._\n_- A fraudulent mortgage or deed was recorded for your house._\n_- You entered into a rent-to-own agreement and have paid all or some of the sale price for the house._", + "label": "Text view", + "type": "text", + "layout": { + "row": "Row_13fd6tp", + "columns": null + }, + "id": "Field_18tio75", + "conditional": { + "hide": "=list contains([null, true], inputs.homeowner)" + } + }, + { + "label": "Do you live in the property as your primary residence?", + "type": "radio", + "layout": { + "row": "Row_1kqy7kx", + "columns": null + }, + "id": "Field_16c17vp", + "key": "inputs.ownerOccupant", + "valuesExpression": "=[\n {label: \"Yes\", value: true},\n {label: \"No\", value: false}\n]", + "conditional": { + "hide": "=list contains([null, false], inputs.homeowner) and list contains([null, false], inputs.equitableInterestInProperty)" + }, + "validate": { + "required": true + } + }, + { + "label": "Have you continuously owned and occupied your property as your primary residence since at least July 1, 2014?", + "type": "radio", + "layout": { + "row": "Row_1n32unh", + "columns": null + }, + "id": "Field_1xsoz3q", + "key": "inputs.tenOrMoreYearsOwnerOccupant", + "valuesExpression": "=[\n {label: \"Yes\", value: true},\n {label: \"No\", value: false}\n]", + "conditional": { + "hide": "=list contains([null, false], inputs.ownerOccupant)" + }, + "validate": { + "required": true + } + }, + { + "label": "Have your property tax assessments increased by at least 50% from last year or increased by at least 75% in the last five years?", + "type": "radio", + "layout": { + "row": "Row_1tq10lk", + "columns": null + }, + "id": "Field_1gxhl3z", + "key": "inputs.loopTaxAssessmentEligible", + "conditional": { + "hide": "=list contains([null, false], inputs.tenOrMoreYearsOwnerOccupant)" + }, + "valuesExpression": "=[ \n {label: \"Yes\", value: true},\n {label: \"No\", value: false}\n]", + "description": "If unsure, select \"Yes\" to proceed. You can verify LOOP tax assessment eligibility before applying at https://property.phila.gov/", + "validate": { + "required": true + }, + "properties": {} + }, + { + "label": "Are you current on your property taxes?", + "type": "radio", + "layout": { + "row": "Row_0zah1f3", + "columns": null + }, + "id": "Field_08xr3il", + "key": "inputs.notTaxDelinquent", + "valuesExpression": "=[\n {label: \"Yes\", value: true},\n {label: \"No\", value: false}\n]", + "conditional": { + "hide": "=list contains([null, false], inputs.ownerOccupant)" + }, + "validate": { + "required": true + } + }, + { + "label": "Have you benefited from the 10-year tax abatement?", + "type": "radio", + "layout": { + "row": "Row_0e0f3vp", + "columns": null + }, + "id": "Field_1na9bcg", + "key": "inputs.tenYearTaxAbatement", + "valuesExpression": "=[\n {label: \"Yes\", value: true},\n {label: \"No\", value: false}\n]", + "description": "(while you owned the property OR received the property from a relative who benefited)", + "conditional": { + "hide": "=list contains([null, false], inputs.ownerOccupant)" + }, + "validate": { + "required": true + } + }, + { + "subtype": "date", + "dateLabel": "What is your date of birth?", + "type": "datetime", + "layout": { + "row": "Row_1x7ahky", + "columns": null + }, + "id": "Field_0q55dt1", + "key": "inputs.dateOfBirth", + "validate": { + "required": true + }, + "conditional": { + "hide": "=list contains([null, false], inputs.ownerOccupant)" + } + }, + { + "label": "Are you married?", + "type": "radio", + "layout": { + "row": "Row_1ppn0aq", + "columns": null + }, + "id": "Field_0fm2hvu", + "key": "inputs.married", + "valuesExpression": "=[\n {label: \"Yes\", value: true},\n {label: \"No\", value: false}\n]", + "validate": { + "required": true + }, + "conditional": { + "hide": "=list contains([null, false], inputs.ownerOccupant)" + } + }, + { + "subtype": "date", + "dateLabel": "What is your spouse's date of birth?", + "type": "datetime", + "layout": { + "row": "Row_0a0lcbi", + "columns": null + }, + "id": "Field_1mjgliz", + "key": "inputs.spouseDateOfBirth", + "conditional": { + "hide": "=list contains([null, false], inputs.married) or years and months duration(date(inputs.dateOfBirth), date(\"2025-12-31\")).years >= 65" + }, + "validate": { + "required": true + } + }, + { + "label": "Are you a widow or widower of someone who reached the age of 65 before passing away?", + "type": "radio", + "layout": { + "row": "Row_1femxtb", + "columns": null + }, + "id": "Field_0v20f5n", + "key": "inputs.lateSpouseWasAtLeastSixtyFive", + "valuesExpression": "=[\n {label: \"Yes\", value: true},\n {label: \"No\", value: false}\n]", + "conditional": { + "hide": "=list contains([null, true], inputs.married) or years and months duration(date(inputs.dateOfBirth), date(\"2025-12-31\")).years < 50 or years and months duration(date(inputs.dateOfBirth), date(\"2025-12-31\")).years >=65" + }, + "validate": { + "required": true + } + }, + { + "label": "How many people are in your household?", + "type": "select", + "layout": { + "row": "Row_1ctf68k", + "columns": null + }, + "id": "Field_1iqiogg", + "key": "inputs.householdSize", + "valuesExpression": "=if inputs.married = true then [2,3,4,5,6,7,8,9,10,11,12,13,14] else [1,2,3,4,5,6,7,8,9,10,11,12,13,14]", + "conditional": { + "hide": "=inputs.married = null" + }, + "validate": { + "required": true + }, + "searchable": true + }, + { + "label": "=if inputs.married then\n \"How much income (gross amount) do you and your spouse currently receive each month?\"\nelse\n \"How much income (gross amount) do you currently receive each month?\"", + "type": "number", + "layout": { + "row": "Row_1eok208", + "columns": null + }, + "id": "Field_1hfn61z", + "key": "inputs.currentMonthlyPrimaryAndSpouseGrossIncome", + "description": "(do not include income received by other members of your household)", + "decimalDigits": 0, + "validate": { + "required": true + }, + "appearance": { + "prefixAdorner": "$" + }, + "conditional": { + "hide": "=inputs.married = null or inputs.householdSize = null" + } + }, + { + "label": "How much total income (gross amount) did your household receive last year (2024)?", + "type": "number", + "layout": { + "row": "Row_0xq286j", + "columns": null + }, + "id": "Field_105uwzg", + "key": "inputs.previousYearGrossHouseholdIncome", + "description": "(Include income received from all household members last year.)", + "validate": { + "required": true + }, + "conditional": { + "hide": "=list contains([null, false],inputs.loopTaxAssessmentEligible) or inputs.currentMonthlyPrimaryAndSpouseGrossIncome = null" + }, + "appearance": { + "prefixAdorner": "$" + } + }, + { + "text": "### You've finished the screener!", + "label": "Text view", + "type": "text", + "layout": { + "row": "Row_1linv0m", + "columns": 12 + }, + "id": "Field_0nrmf4u", + "conditional": { + "hide": "=not(form.complete)" + } + }, + { + "label": "Screen again", + "action": "reset", + "type": "button", + "layout": { + "row": "Row_0i63nhn", + "columns": null + }, + "id": "Field_1v46rc1", + "conditional": { + "hide": "=not(form.complete)" + } + } + ], + "showOutline": false, + "type": "group", + "layout": { + "row": "Row_14395zo", + "columns": null + }, + "id": "Field_15slsxg" + }, + { + "computeOn": "change", + "label": "Expression", + "type": "expression", + "layout": { + "row": "Row_18xb3fc", + "columns": null + }, + "id": "Field_0lib806", + "key": "form.complete", + "expression": "=every entry in (get entries(inputs)) satisfies\n not(list contains([null,\"\"], entry.value)) and entry.value != []\n" + }, + { + "computeOn": "change", + "label": "Expression", + "type": "expression", + "layout": { + "row": "Row_18xb3fc", + "columns": null + }, + "id": "Field_1ufxr3s", + "key": "form.empty", + "expression": "=every entry in (get entries(inputs)) satisfies (entry.value = null)" + }, + { + "label": "LOOP Results", + "components": [ + { + "text": "# LOOP: likely eligible\n\nYou appear eligible for the Longtime Owner Occupants Program (LOOP), which is a Real Estate Tax relief program. LOOP works by:\n\n1. Limiting your home’s assessment increase to 50% (or 1.5 times the previous amount) or 75% (or 1.75 times the previous amount).\n2. Locking in that assessment for as long as you remain eligible.\n\n**Benefit Amount:** Visit https://property.phila.gov/ and enter your address to see your estimated property tax bill savings under this benefit \n\n**Application forms:** https://www.phila.gov/documents/longtime-owner-occupants-program-loop-forms/\n\n**Apply online:** https://tax-services.phila.gov/_/\n\n**Questions?** Call the Philadelphia Department of Revenue at 215-686-9200\n\n**NOTE:** You cannot also receive LOOP while receiving a homestead exemption. To see which program is better for you, compare your potential savings by visting https://property.phila.gov/ or calling the phone number above for more information. See example pictures below of the different estimations for Homestead vs LOOP.\n \t", + "type": "text", + "layout": { + "row": "Row_0kgw1fp", + "columns": null + }, + "id": "Field_13ekkem", + "conditional": { + "hide": "=benefits.phlLoop.eligibility.result != true " + } + }, + { + "type": "image", + "layout": { + "row": "Row_0dkgxod", + "columns": null + }, + "id": "Field_05qfili", + "source": "https://lh3.googleusercontent.com/pw/AP1GczO6sRQKSb8PQndU4mUt4tpUxH1PJO_b_RcfjBuF8Nghrp4vEj2-X3lGUAAEb1hbZ6rcCnoffL15gobdlOsu8TbT8nUpdDQkWvtGivmxWZZIW-ypC8xayiqfwlcqurqeo8TasH2csqJZJOlp4YvffQE=w912-h806-s-no-gm?", + "conditional": { + "hide": "=benefits.phlLoop.eligibility.result != true " + } + }, + { + "type": "image", + "layout": { + "row": "Row_0qney0k", + "columns": null + }, + "id": "Field_0qqvagc", + "source": "https://lh3.googleusercontent.com/pw/AP1GczM9u7rFIRrVIz9CBxgJKbwpeZBIj9TnZQA6wwGhLW5DIiYt1LSHrRNo2cpslkxJ89x1wHVibNJ1GVelPp2Dx4WNspPTa-8xph-55_cm1onNI52Z3uyVMIAUV2azxDhfzzLz556Vp2XH9MG802dLbpc=w913-h778-s-no-gm?", + "conditional": { + "hide": "=benefits.phlLoop.eligibility.result != true " + } + }, + { + "text": "# LOOP: Ineligible\n\nYou appear to be either already enrolled or ineligible for this tax relief benefit based on the information provided. \n\nIf you feel this is an error you can complete a new screening, or apply for an official determination of eligibility with the Philadelphia Department of Revenue. Visit the Philadelphia Department of Revenue website or call them at (215) 686-6442 for more information.", + "type": "text", + "layout": { + "row": "Row_0xoszsh", + "columns": 16 + }, + "id": "Field_128l3pp", + "conditional": { + "hide": "=benefits.phlLoop.eligibility.result = true or benefits.phlLoop.eligibility.result = null " + } + } + ], + "showOutline": true, + "type": "group", + "layout": { + "row": "Row_1j11cc3", + "columns": null + }, + "id": "Field_1682hb3", + "conditional": { + "hide": "=not(form.complete)" + } + }, + { + "label": "=if form.complete then\n \"Results\"\nelse\n \"Eligibility Details\"", + "components": [ + { + "text": "{{#loop get entries(benefits)}}\n#### {{string join(for i in 1..string length(key) return if(i = 1) then upper case(substring(key, i, 1)) else if(substring(key, i, 1) >= \"A\" and substring(key, i, 1) <= \"Z\" and (substring(key, i-1, 1) < \"A\" or substring(key, i-1, 1) > \"Z\")) then \" \" + substring(key, i, 1) else substring(key, i, 1))}} {{if value.eligibility.result = true then \"✅\" else if value.eligibility.result = null then \"❔\" else \"❌\"}}\n{{#loop get entries(value.eligibility.checks)}}\n- {{string join(for i in 1..string length(key) return if(i = 1) then upper case(substring(key, i, 1)) else if(substring(key, i, 1) >= \"A\" and substring(key, i, 1) <= \"Z\" and (substring(key, i-1, 1) < \"A\" or substring(key, i-1, 1) > \"Z\")) then \" \" + substring(key, i, 1) else substring(key, i, 1))}} {{if value = true then \"🟢\" else if value = null then \"🟡\" else \"🔴\"}}\n{{/loop}}\n{{/loop}}", + "label": "Text view", + "type": "text", + "layout": { + "row": "Row_1yx4wpl", + "columns": null + }, + "id": "Field_01v1uwd" + } + ], + "showOutline": true, + "type": "group", + "layout": { + "row": "Row_1yx4wpl", + "columns": null + }, + "id": "Field_results", + "verticalAlignment": "end", + "conditional": { + "hide": "=form.incomplete" + } + }, + { + "label": "=if false then\n \"Placeholder for the results returned from the DMN API. (not meant to be displayed in the deployed form)\"\nelse\n \"\"", + "type": "checkbox", + "layout": { + "row": "Row_1htq3tz", + "columns": null + }, + "id": "Field_benefits-readonly", + "key": "benefits", + "readonly": "true", + "disabled": false + } + ], + "type": "default", + "id": "phlPropertyTaxRelief", + "executionPlatform": "Camunda Cloud", + "executionPlatformVersion": "8.6.0", + "versionTag": "0.0.1", + "exporter": { + "name": "Camunda Modeler", + "version": "5.29.0" + }, + "schemaVersion": 17 +} \ No newline at end of file diff --git a/screener-api/src/main/resources/templates/screener.html b/screener-api/src/main/resources/templates/screener.html new file mode 100644 index 0000000..c7bd76f --- /dev/null +++ b/screener-api/src/main/resources/templates/screener.html @@ -0,0 +1,74 @@ + + + + + + + + +
+
+ +
+

Results:

+

+
+
+ + + + + diff --git a/screener-api/src/main/resources/test.json b/screener-api/src/main/resources/test.json new file mode 100644 index 0000000..9a4b3e3 --- /dev/null +++ b/screener-api/src/main/resources/test.json @@ -0,0 +1,108 @@ +{ + "components": [ + { + "text": "Test Form\n", + "type": "text", + "layout": { + "row": "Row_05roz8z", + "columns": null + }, + "id": "Field_1bw4y77" + }, + { + "label": "Checkbox group", + "values": [ + { + "label": "Value", + "value": "value" + } + ], + "type": "checklist", + "layout": { + "row": "Row_0ie2vd0", + "columns": null + }, + "id": "Field_19xod6n", + "key": "checklist_ai63f" + }, + { + "type": "table", + "layout": { + "row": "Row_0xdlcvc", + "columns": null + }, + "label": "Table", + "rowCount": 10, + "columns": [ + { + "label": "ID", + "key": "id" + }, + { + "label": "Name", + "key": "name" + }, + { + "label": "Date", + "key": "date" + } + ], + "id": "Field_1e5xt2v", + "dataSource": "=Field_1e5xt2v" + }, + { + "label": "Checkbox", + "type": "checkbox", + "layout": { + "row": "Row_1cvg8jt", + "columns": null + }, + "id": "Field_1yhpnx2", + "key": "checkbox_ixztq" + }, + { + "label": "Radio group", + "values": [ + { + "label": "Value", + "value": "value" + } + ], + "type": "radio", + "layout": { + "row": "Row_0rf7mmj", + "columns": null + }, + "id": "Field_0f84rcb", + "key": "radio_dlkf6l" + }, + { + "subtype": "date", + "dateLabel": "Date", + "type": "datetime", + "layout": { + "row": "Row_1m0n00k", + "columns": null + }, + "id": "Field_06l21hg", + "key": "datetime_qzw3q" + }, + { + "label": "File picker", + "type": "filepicker", + "layout": { + "row": "Row_0zcc8zv", + "columns": null + }, + "id": "Field_1j2mgn3", + "key": "filepicker_uf5oqr" + } + ], + "type": "default", + "id": "Form_0g042cw", + "exporter": { + "name": "form-js (https://demo.bpmn.io)", + "version": "1.15.0" + }, + "schemaVersion": 18 +} \ No newline at end of file diff --git a/screener-api/src/test/java/org/acme/GreetingResourceIT.java b/screener-api/src/test/java/org/acme/GreetingResourceIT.java new file mode 100644 index 0000000..415ad4e --- /dev/null +++ b/screener-api/src/test/java/org/acme/GreetingResourceIT.java @@ -0,0 +1,8 @@ +package org.acme; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +//@QuarkusIntegrationTest +//class GreetingResourceIT extends GreetingResourceTest { +// // Execute the same tests but in packaged mode. +//} diff --git a/screener-api/src/test/java/org/acme/GreetingResourceTest.java b/screener-api/src/test/java/org/acme/GreetingResourceTest.java new file mode 100644 index 0000000..2a00736 --- /dev/null +++ b/screener-api/src/test/java/org/acme/GreetingResourceTest.java @@ -0,0 +1,20 @@ +package org.acme; + +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.Test; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +//@QuarkusTest +//class GreetingResourceTest { +// @Test +// void testHelloEndpoint() { +// given() +// .when().get("/hello") +// .then() +// .statusCode(200) +// .body(is("Hello from Quarkus REST")); +// } +// +//} \ No newline at end of file diff --git a/screener-frontend/.gitignore b/screener-frontend/.gitignore new file mode 100644 index 0000000..f79022d --- /dev/null +++ b/screener-frontend/.gitignore @@ -0,0 +1,25 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +deployment.txt \ No newline at end of file diff --git a/screener-frontend/README.md b/screener-frontend/README.md new file mode 100644 index 0000000..167c567 --- /dev/null +++ b/screener-frontend/README.md @@ -0,0 +1,28 @@ +## Usage + +```bash +$ npm install # or pnpm install or yarn install +``` + +### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs) + +## Available Scripts + +In the project directory, you can run: + +### `npm run dev` + +Runs the app in the development mode.
+Open [http://localhost:5173](http://localhost:5173) to view it in the browser. + +### `npm run build` + +Builds the app for production to the `dist` folder.
+It correctly bundles Solid in production mode and optimizes the build for the best performance. + +The build is minified and the filenames include the hashes.
+Your app is ready to be deployed! + +## Deployment + +Learn more about deploying your application with the [documentations](https://vite.dev/guide/static-deploy.html) diff --git a/screener-frontend/index.html b/screener-frontend/index.html new file mode 100644 index 0000000..c5e49ef --- /dev/null +++ b/screener-frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + Solid + + +
+ + + diff --git a/screener-frontend/package-lock.json b/screener-frontend/package-lock.json new file mode 100644 index 0000000..0bfec79 --- /dev/null +++ b/screener-frontend/package-lock.json @@ -0,0 +1,2889 @@ +{ + "name": "screener-frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "screener-frontend", + "version": "0.0.0", + "dependencies": { + "@bpmn-io/form-js": "^1.15.2", + "@solidjs/router": "^0.15.3", + "@tailwindcss/vite": "^4.1.7", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lodash.debounce": "^4.0.8", + "solid-js": "^1.9.5", + "tailwind-merge": "^3.3.0", + "tailwindcss": "^4.1.7", + "tailwindcss-animate": "^1.0.7" + }, + "devDependencies": { + "@types/node": "^22.15.18", + "vite": "^6.3.5", + "vite-plugin-solid": "^2.11.6" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz", + "integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz", + "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helpers": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", + "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz", + "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", + "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", + "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.1" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", + "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", + "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bpmn-io/cm-theme": { + "version": "0.1.0-alpha.2", + "resolved": "https://registry.npmjs.org/@bpmn-io/cm-theme/-/cm-theme-0.1.0-alpha.2.tgz", + "integrity": "sha512-ZILgiYzxk3KMvxplUXmdRFQo45/JehDPg5k9tWfehmzUOSE13ssyLPil8uCloMQnb3yyzyOWTjb/wzKXTHlFQw==", + "dependencies": { + "@codemirror/language": "^6.3.1", + "@codemirror/view": "^6.5.1", + "@lezer/highlight": "^1.1.4" + } + }, + "node_modules/@bpmn-io/draggle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@bpmn-io/draggle/-/draggle-4.1.1.tgz", + "integrity": "sha512-2frw1gBl5I3XGrIDg4CBy6bpJiOuslKUOg9T91Fke6bIttFkF0zxlTKh4E4zU8g7gAo4ze0HnKMZDgHxea+Itw==", + "dependencies": { + "contra": "^1.9.4" + } + }, + "node_modules/@bpmn-io/feel-editor": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@bpmn-io/feel-editor/-/feel-editor-1.10.0.tgz", + "integrity": "sha512-Unc4CSyMgDg5c2C3E3ehEbJZfyo5W9Zrq74C8cp7mjFbb3if6rTBaw3ZCZeiC06zsm881sI5P8zWHFdIhKo/vA==", + "dependencies": { + "@bpmn-io/feel-lint": "^1.4.0", + "@codemirror/autocomplete": "^6.16.2", + "@codemirror/commands": "^6.8.0", + "@codemirror/language": "^6.10.2", + "@codemirror/lint": "^6.8.4", + "@codemirror/state": "^6.5.1", + "@codemirror/view": "^6.36.2", + "@lezer/highlight": "^1.2.1", + "lang-feel": "^2.3.0", + "min-dom": "^4.2.1" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/@bpmn-io/feel-lint": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@bpmn-io/feel-lint/-/feel-lint-1.4.0.tgz", + "integrity": "sha512-1bsdR/9vPD7RQVqWWPk0X0tpjLsYTDrCxIzOVtN/h32o4nPGl0dZBU5m07qaFUGD4wG3eOH4Qim1wexHG8YkBw==", + "dependencies": { + "@codemirror/language": "^6.10.8", + "lezer-feel": "^1.7.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@bpmn-io/form-js": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@bpmn-io/form-js/-/form-js-1.15.2.tgz", + "integrity": "sha512-aiGgzSYuG85AtYfXxJUYfeRpSvDO7FPyZk9lstcFja0jJirDuZC/xlQfH4Ywp9wXNg2kmeXjPBj3Nny+92x6hQ==", + "dependencies": { + "@bpmn-io/form-js-carbon-styles": "^1.15.2", + "@bpmn-io/form-js-editor": "^1.15.2", + "@bpmn-io/form-js-playground": "^1.15.2", + "@bpmn-io/form-js-viewer": "^1.15.2" + } + }, + "node_modules/@bpmn-io/form-js-carbon-styles": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@bpmn-io/form-js-carbon-styles/-/form-js-carbon-styles-1.15.2.tgz", + "integrity": "sha512-QUpJA2hXs8SMmiDtKXRSajEZosPpX4DKBLKPGS6litiPTgJ7EmhoNtGnz3WWsJL4SZR9JgZ/ObYNMzuDAESbng==" + }, + "node_modules/@bpmn-io/form-js-editor": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@bpmn-io/form-js-editor/-/form-js-editor-1.15.2.tgz", + "integrity": "sha512-iomcTAkXxUy8X4ARUwSNzFRP0XEfyj4PCdKrNgR3gnUygcRufi/rS/BFj+8mRGKUdCGV+mDzEqshDX0mL0zLgQ==", + "dependencies": { + "@bpmn-io/draggle": "^4.1.1", + "@bpmn-io/form-js-viewer": "^1.15.2", + "@bpmn-io/properties-panel": "^3.26.3", + "array-move": "^4.0.0", + "big.js": "^6.2.2", + "ids": "^1.0.5", + "min-dash": "^4.2.3", + "min-dom": "^4.1.0", + "preact": "^10.5.14" + } + }, + "node_modules/@bpmn-io/form-js-playground": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@bpmn-io/form-js-playground/-/form-js-playground-1.15.2.tgz", + "integrity": "sha512-lV9fYL1v8hyfx5MeY7Npk2hL124AkQzld52eUE7RYPPqr0OSxaDqCTNM1y3awUdhDnc97fDn45tQoiJw1LcoRA==", + "dependencies": { + "@bpmn-io/form-js-editor": "^1.15.2", + "@bpmn-io/form-js-viewer": "^1.15.2", + "@codemirror/autocomplete": "^6.18.6", + "@codemirror/commands": "^6.8.0", + "@codemirror/lang-json": "^6.0.1", + "@codemirror/language": "^6.11.0", + "@codemirror/lint": "^6.8.4", + "@codemirror/state": "^6.5.2", + "@codemirror/view": "^6.36.4", + "classnames": "^2.5.1", + "codemirror": "^6.0.1", + "downloadjs": "^1.4.7", + "file-drops": "^0.5.0", + "mitt": "^3.0.1", + "preact": "^10.5.14" + } + }, + "node_modules/@bpmn-io/form-js-viewer": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@bpmn-io/form-js-viewer/-/form-js-viewer-1.15.2.tgz", + "integrity": "sha512-Es5cl+F8WokhpNAsn45uYT9PPVl+iODNW9TugP6o2atsOZf+dz2JvPHPVGSZ1g5K6ZPRqg4xQcpzdsR7w4RJKA==", + "dependencies": { + "@carbon/grid": "^11.32.2", + "big.js": "^6.2.2", + "classnames": "^2.5.1", + "didi": "^10.2.2", + "dompurify": "^3.2.4", + "feelers": "^1.4.0", + "feelin": "^4.3.0", + "flatpickr": "^4.6.13", + "ids": "^1.0.5", + "lodash": "^4.17.21", + "luxon": "^3.5.0", + "marked": "^15.0.7", + "min-dash": "^4.2.3", + "preact": "^10.5.14" + } + }, + "node_modules/@bpmn-io/properties-panel": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@bpmn-io/properties-panel/-/properties-panel-3.27.0.tgz", + "integrity": "sha512-Us1d3mP2CePMG3V0ElpPVoKuTN+ukxhb0Cjj6prxoRsEP2uWm0H62/tPsYc3SNdGcUb/YOdryBJLL+OcM/GaQw==", + "dependencies": { + "@bpmn-io/feel-editor": "^1.10.0", + "@codemirror/view": "^6.28.1", + "classnames": "^2.3.1", + "feelers": "^1.4.0", + "focus-trap": "^7.5.2", + "min-dash": "^4.1.1", + "min-dom": "^4.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@carbon/grid": { + "version": "11.36.0", + "resolved": "https://registry.npmjs.org/@carbon/grid/-/grid-11.36.0.tgz", + "integrity": "sha512-wYSHLnTw0rCjumBm/OB8H8H/rJR0MJpvBCnBtK6BCPh/vBTnpFX05MgzskZISkoAUomLbExbI6PtxfT5ZmgDMg==", + "hasInstallScript": true, + "dependencies": { + "@carbon/layout": "^11.34.0", + "@ibm/telemetry-js": "^1.5.0" + } + }, + "node_modules/@carbon/layout": { + "version": "11.34.0", + "resolved": "https://registry.npmjs.org/@carbon/layout/-/layout-11.34.0.tgz", + "integrity": "sha512-6PMofb/Ul/CZkP1XivszK2teh0odPV/bUG/PPQlVX1xnFdc+4O9o08JnFG9N2Ie6syncOXRCf8z1CegpV28x9A==", + "hasInstallScript": true, + "dependencies": { + "@ibm/telemetry-js": "^1.5.0" + } + }, + "node_modules/@codemirror/autocomplete": { + "version": "6.18.6", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz", + "integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz", + "integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-json": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz", + "integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/json": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.0.tgz", + "integrity": "sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.8.5", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz", + "integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.35.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.5.11", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz", + "integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", + "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.36.8", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.8.tgz", + "integrity": "sha512-yoRo4f+FdnD01fFt4XpfpMCcCAo9QvZOtbrXExn4SqzH32YC6LgzqxfLZw/r6Ge65xyY03mK/UfUqrVw1gFiFg==", + "dependencies": { + "@codemirror/state": "^6.5.0", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", + "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", + "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", + "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", + "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", + "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", + "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", + "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", + "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", + "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", + "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", + "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", + "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", + "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", + "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", + "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", + "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", + "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", + "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", + "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", + "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", + "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", + "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", + "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", + "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", + "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@ibm/telemetry-js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@ibm/telemetry-js/-/telemetry-js-1.9.1.tgz", + "integrity": "sha512-qq8RPafUJHUQieXVCte1kbJEx6JctWzbA/YkXzopbfzIDRT2+hbR9QmgH+KH7bDDNRcDbdHWvHfwJKzThlMtPg==", + "bin": { + "ibmtelemetry": "dist/collect.js" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lezer/common": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", + "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==" + }, + "node_modules/@lezer/highlight": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", + "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/json": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz", + "integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", + "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/markdown": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.4.3.tgz", + "integrity": "sha512-kfw+2uMrQ/wy/+ONfrH83OkdFNM0ye5Xq96cLlaCy7h5UT9FO54DU4oRoIc0CSBh5NWmWuiIJA7NGLMJbQ+Oxg==", + "dependencies": { + "@lezer/common": "^1.0.0", + "@lezer/highlight": "^1.0.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.2.tgz", + "integrity": "sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.2.tgz", + "integrity": "sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.2.tgz", + "integrity": "sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.2.tgz", + "integrity": "sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.2.tgz", + "integrity": "sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.2.tgz", + "integrity": "sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.2.tgz", + "integrity": "sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.2.tgz", + "integrity": "sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.2.tgz", + "integrity": "sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.2.tgz", + "integrity": "sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.2.tgz", + "integrity": "sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.2.tgz", + "integrity": "sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.2.tgz", + "integrity": "sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.2.tgz", + "integrity": "sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.2.tgz", + "integrity": "sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.2.tgz", + "integrity": "sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.2.tgz", + "integrity": "sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.2.tgz", + "integrity": "sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.2.tgz", + "integrity": "sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.2.tgz", + "integrity": "sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@solidjs/router": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/@solidjs/router/-/router-0.15.3.tgz", + "integrity": "sha512-iEbW8UKok2Oio7o6Y4VTzLj+KFCmQPGEpm1fS3xixwFBdclFVBvaQVeibl1jys4cujfAK5Kn6+uG2uBm3lxOMw==", + "peerDependencies": { + "solid-js": "^1.8.6" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.7.tgz", + "integrity": "sha512-9rsOpdY9idRI2NH6CL4wORFY0+Q6fnx9XP9Ju+iq/0wJwGD5IByIgFmwVbyy4ymuyprj8Qh4ErxMKTUL4uNh3g==", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.30.1", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.7" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.7.tgz", + "integrity": "sha512-5SF95Ctm9DFiUyjUPnDGkoKItPX/k+xifcQhcqX5RA85m50jw1pT/KzjdvlqxRja45Y52nR4MR9fD1JYd7f8NQ==", + "hasInstallScript": true, + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.7", + "@tailwindcss/oxide-darwin-arm64": "4.1.7", + "@tailwindcss/oxide-darwin-x64": "4.1.7", + "@tailwindcss/oxide-freebsd-x64": "4.1.7", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.7", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.7", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.7", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.7", + "@tailwindcss/oxide-linux-x64-musl": "4.1.7", + "@tailwindcss/oxide-wasm32-wasi": "4.1.7", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.7", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.7" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.7.tgz", + "integrity": "sha512-IWA410JZ8fF7kACus6BrUwY2Z1t1hm0+ZWNEzykKmMNM09wQooOcN/VXr0p/WJdtHZ90PvJf2AIBS/Ceqx1emg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.7.tgz", + "integrity": "sha512-81jUw9To7fimGGkuJ2W5h3/oGonTOZKZ8C2ghm/TTxbwvfSiFSDPd6/A/KE2N7Jp4mv3Ps9OFqg2fEKgZFfsvg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.7.tgz", + "integrity": "sha512-q77rWjEyGHV4PdDBtrzO0tgBBPlQWKY7wZK0cUok/HaGgbNKecegNxCGikuPJn5wFAlIywC3v+WMBt0PEBtwGw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.7.tgz", + "integrity": "sha512-RfmdbbK6G6ptgF4qqbzoxmH+PKfP4KSVs7SRlTwcbRgBwezJkAO3Qta/7gDy10Q2DcUVkKxFLXUQO6J3CRvBGw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.7.tgz", + "integrity": "sha512-OZqsGvpwOa13lVd1z6JVwQXadEobmesxQ4AxhrwRiPuE04quvZHWn/LnihMg7/XkN+dTioXp/VMu/p6A5eZP3g==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.7.tgz", + "integrity": "sha512-voMvBTnJSfKecJxGkoeAyW/2XRToLZ227LxswLAwKY7YslG/Xkw9/tJNH+3IVh5bdYzYE7DfiaPbRkSHFxY1xA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.7.tgz", + "integrity": "sha512-PjGuNNmJeKHnP58M7XyjJyla8LPo+RmwHQpBI+W/OxqrwojyuCQ+GUtygu7jUqTEexejZHr/z3nBc/gTiXBj4A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.7.tgz", + "integrity": "sha512-HMs+Va+ZR3gC3mLZE00gXxtBo3JoSQxtu9lobbZd+DmfkIxR54NO7Z+UQNPsa0P/ITn1TevtFxXTpsRU7qEvWg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.7.tgz", + "integrity": "sha512-MHZ6jyNlutdHH8rd+YTdr3QbXrHXqwIhHw9e7yXEBcQdluGwhpQY2Eku8UZK6ReLaWtQ4gijIv5QoM5eE+qlsA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.7.tgz", + "integrity": "sha512-ANaSKt74ZRzE2TvJmUcbFQ8zS201cIPxUDm5qez5rLEwWkie2SkGtA4P+GPTj+u8N6JbPrC8MtY8RmJA35Oo+A==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@emnapi/wasi-threads": "^1.0.2", + "@napi-rs/wasm-runtime": "^0.2.9", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.7.tgz", + "integrity": "sha512-HUiSiXQ9gLJBAPCMVRk2RT1ZrBjto7WvqsPBwUrNK2BcdSxMnk19h4pjZjI7zgPhDxlAbJSumTC4ljeA9y0tEw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.7.tgz", + "integrity": "sha512-rYHGmvoHiLJ8hWucSfSOEmdCBIGZIq7SpkPRSqLsH2Ab2YUNgKeAPT1Fi2cx3+hnYOrAb0jp9cRyode3bBW4mQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.7.tgz", + "integrity": "sha512-tYa2fO3zDe41I7WqijyVbRd8oWT0aEID1Eokz5hMT6wShLIHj3yvwj9XbfuloHP9glZ6H+aG2AN/+ZrxJ1Y5RQ==", + "dependencies": { + "@tailwindcss/node": "4.1.7", + "@tailwindcss/oxide": "4.1.7", + "tailwindcss": "4.1.7" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==" + }, + "node_modules/@types/node": { + "version": "22.15.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.18.tgz", + "integrity": "sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg==", + "devOptional": true, + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "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==", + "optional": true + }, + "node_modules/array-move": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/array-move/-/array-move-4.0.0.tgz", + "integrity": "sha512-+RY54S8OuVvg94THpneQvFRmqWdAHeqtMzgMW6JNurHxe8rsS07cHQdfGkXnTUXiBcyZ0j3SiDIxxj0RPiqCkQ==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/atoa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atoa/-/atoa-1.0.0.tgz", + "integrity": "sha512-VVE1H6cc4ai+ZXo/CRWoJiHXrA1qfA31DPnx6D20+kSI547hQN5Greh51LQ1baMRMfxO5K5M4ImMtZbZt2DODQ==" + }, + "node_modules/babel-plugin-jsx-dom-expressions": { + "version": "0.39.8", + "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.39.8.tgz", + "integrity": "sha512-/MVOIIjonylDXnrWmG23ZX82m9mtKATsVHB7zYlPfDR9Vdd/NBE48if+wv27bSkBtyO7EPMUlcUc4J63QwuACQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "7.18.6", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/types": "^7.20.7", + "html-entities": "2.3.3", + "parse5": "^7.1.2", + "validate-html-nesting": "^1.2.1" + }, + "peerDependencies": { + "@babel/core": "^7.20.12" + } + }, + "node_modules/babel-plugin-jsx-dom-expressions/node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/babel-preset-solid": { + "version": "1.9.6", + "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.9.6.tgz", + "integrity": "sha512-HXTK9f93QxoH8dYn1M2mJdOlWgMsR88Lg/ul6QCZGkNTktjTE5HAf93YxQumHoCudLEtZrU1cFCMFOVho6GqFg==", + "dev": true, + "dependencies": { + "babel-plugin-jsx-dom-expressions": "^0.39.8" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/big.js": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-6.2.2.tgz", + "integrity": "sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==", + "engines": { + "node": "*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bigjs" + } + }, + "node_modules/browserslist": { + "version": "4.24.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz", + "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001716", + "electron-to-chromium": "^1.5.149", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001718", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz", + "integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "engines": { + "node": ">=18" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/codemirror": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", + "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, + "node_modules/component-event": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/component-event/-/component-event-0.2.1.tgz", + "integrity": "sha512-wGA++isMqiDq1jPYeyv2as/Bt/u+3iLW0rEa+8NQ82jAv3TgqMiCM+B2SaBdn2DfLilLjjq736YcezihRYhfxw==" + }, + "node_modules/contra": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/contra/-/contra-1.9.4.tgz", + "integrity": "sha512-N9ArHAqwR/lhPq4OdIAwH4e1btn6EIZMAz4TazjnzCiVECcWUPTma+dRAM38ERImEJBh8NiCCpjoQruSZ+agYg==", + "dependencies": { + "atoa": "1.0.0", + "ticky": "1.0.1" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/didi": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/didi/-/didi-10.2.2.tgz", + "integrity": "sha512-l8NYkYFXV1izHI65EyT8EXOjUZtKmQkHLTT89cSP7HU5J/G7AOj0dXKtLc04EXYlga99PBY18IPjOeZ+c3DI4w==", + "engines": { + "node": ">= 16" + } + }, + "node_modules/domify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/domify/-/domify-1.4.2.tgz", + "integrity": "sha512-m4yreHcUWHBncGVV7U+yQzc12vIlq0jMrtHZ5mW6dQMiL/7skSYNVX9wqKwOtyO9SGCgevrAFEgOCAHmamHTUA==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dompurify": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.5.tgz", + "integrity": "sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ==", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/downloadjs": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/downloadjs/-/downloadjs-1.4.7.tgz", + "integrity": "sha512-LN1gO7+u9xjU5oEScGFKvXhYf7Y/empUIIEAGBs1LzUq/rg5duiDrkuH5A2lQGd5jfMOb9X9usDa2oVXwJ0U/Q==" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.155", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz", + "integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==", + "dev": true + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", + "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", + "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.4", + "@esbuild/android-arm": "0.25.4", + "@esbuild/android-arm64": "0.25.4", + "@esbuild/android-x64": "0.25.4", + "@esbuild/darwin-arm64": "0.25.4", + "@esbuild/darwin-x64": "0.25.4", + "@esbuild/freebsd-arm64": "0.25.4", + "@esbuild/freebsd-x64": "0.25.4", + "@esbuild/linux-arm": "0.25.4", + "@esbuild/linux-arm64": "0.25.4", + "@esbuild/linux-ia32": "0.25.4", + "@esbuild/linux-loong64": "0.25.4", + "@esbuild/linux-mips64el": "0.25.4", + "@esbuild/linux-ppc64": "0.25.4", + "@esbuild/linux-riscv64": "0.25.4", + "@esbuild/linux-s390x": "0.25.4", + "@esbuild/linux-x64": "0.25.4", + "@esbuild/netbsd-arm64": "0.25.4", + "@esbuild/netbsd-x64": "0.25.4", + "@esbuild/openbsd-arm64": "0.25.4", + "@esbuild/openbsd-x64": "0.25.4", + "@esbuild/sunos-x64": "0.25.4", + "@esbuild/win32-arm64": "0.25.4", + "@esbuild/win32-ia32": "0.25.4", + "@esbuild/win32-x64": "0.25.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/feelers": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/feelers/-/feelers-1.4.0.tgz", + "integrity": "sha512-CGa/7ILuqoqTaeYeoKsg/4tzu2es9sEEJTmSjdu0lousZBw4V9gcYhHYFNmbrSrKmbAVfOzj6/DsymGJWFIOeg==", + "dependencies": { + "@bpmn-io/cm-theme": "^0.1.0-alpha.2", + "@bpmn-io/feel-lint": "^1.2.0", + "@codemirror/autocomplete": "^6.10.1", + "@codemirror/commands": "^6.3.0", + "@codemirror/language": "^6.9.1", + "@codemirror/lint": "^6.4.2", + "@codemirror/state": "^6.3.0", + "@codemirror/view": "^6.21.3", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.1.6", + "@lezer/lr": "^1.3.13", + "@lezer/markdown": "^1.1.0", + "feelin": "^3.0.1", + "lezer-feel": "^1.2.4", + "min-dom": "^5.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/feelers/node_modules/domify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/domify/-/domify-2.0.0.tgz", + "integrity": "sha512-rmvrrmWQPD/X1A/nPBfIVg4r05792QdG9Z4Prk6oQG0F9zBMDkr0GKAdds1wjb2dq1rTz/ywc4ZxpZbgz0tttg==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/feelers/node_modules/feelin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/feelin/-/feelin-3.2.0.tgz", + "integrity": "sha512-GFDbHsTYk7YXO1tyw1dOjb7IODeAZvNIosdGZThUwPx5XcD/XhO0hnPZXsIbAzSsIdrgGlTEEdby9fZ2gixysA==", + "dependencies": { + "@lezer/lr": "^1.4.2", + "lezer-feel": "^1.4.0", + "luxon": "^3.5.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/feelers/node_modules/min-dom": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/min-dom/-/min-dom-5.1.1.tgz", + "integrity": "sha512-GaKUlguMAofd3OJsB0OkP17i5kucKqErgVCJxPawO9l5NwIPnr28SAr99zzlzMCWWljISBYrnZVWdE2Q92YGFQ==", + "dependencies": { + "domify": "^2.0.0", + "min-dash": "^4.2.1" + } + }, + "node_modules/feelin": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/feelin/-/feelin-4.3.0.tgz", + "integrity": "sha512-Uv3YNCrmXSXgkCJSfa2jR7SL/acjPhfEHKNsaL///fRJxD7epUvUJyF1WXbh8oh9Myv+z3WVClGb+rIuT7LDow==", + "dependencies": { + "@lezer/lr": "^1.4.2", + "lezer-feel": "^1.6.0", + "luxon": "^3.5.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/file-drops": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/file-drops/-/file-drops-0.5.0.tgz", + "integrity": "sha512-ZaENKwVySae4RhEGjh1gEE1wMnIIPG6XqtOwHNQYSl7RNwUHoRGVVspe+BrW7cUFseHNIit3Oy9Z/HPIEU5XWA==", + "dependencies": { + "min-dom": "^4.0.3" + } + }, + "node_modules/flatpickr": { + "version": "4.6.13", + "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz", + "integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw==" + }, + "node_modules/focus-trap": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.4.tgz", + "integrity": "sha512-xx560wGBk7seZ6y933idtjJQc1l+ck+pI3sKvhKozdBV1dRZoKhkW5xoCaFv9tQiX5RH1xfSxjuNu6g+lmN/gw==", + "dependencies": { + "tabbable": "^6.2.0" + } + }, + "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, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", + "dev": true + }, + "node_modules/ids": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/ids/-/ids-1.0.5.tgz", + "integrity": "sha512-XQ0yom/4KWTL29sLG+tyuycy7UmeaM/79GRtSJq6IG9cJGIPeBz5kwDCguie3TwxaMNIc3WtPi0cTa1XYHicpw==" + }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "dev": true, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lang-feel": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/lang-feel/-/lang-feel-2.3.0.tgz", + "integrity": "sha512-cotBfyBP710udy3Tm7s4NyNZPSSLXkVV/rrfmM4NVbuzB9WGL7CbMWUzfSn6GZ+qFnh8/xbkeDHfAvPM90oENA==", + "dependencies": { + "@codemirror/autocomplete": "^6.18.4", + "@codemirror/language": "^6.10.8", + "@lezer/common": "^1.2.3", + "lezer-feel": "^1.7.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/lezer-feel": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/lezer-feel/-/lezer-feel-1.7.0.tgz", + "integrity": "sha512-UC8h3Nu4llRPISEUhv+Ne7bNkdxjf4+/DcU4KfO8zKxycWxev8d2BoVnGlG17zbQDtQJBD39ZQvWtjCeTFm69g==", + "dependencies": { + "@lezer/highlight": "^1.2.1", + "@lezer/lr": "^1.4.2", + "min-dash": "^4.2.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/luxon": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.6.1.tgz", + "integrity": "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/marked": { + "version": "15.0.11", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.11.tgz", + "integrity": "sha512-1BEXAU2euRCG3xwgLVT1y0xbJEld1XOrmRJpUwRCcy7rxhSCwMrmEu9LXoPhHSCJG41V7YcQ2mjKRr5BA3ITIA==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/merge-anything": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/merge-anything/-/merge-anything-5.1.7.tgz", + "integrity": "sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==", + "dev": true, + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/min-dash": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/min-dash/-/min-dash-4.2.3.tgz", + "integrity": "sha512-VLMYQI5+FcD9Ad24VcB08uA83B07OhueAlZ88jBK6PyupTvEJwllTMUqMy0wPGYs7pZUEtEEMWdHB63m3LtEcg==" + }, + "node_modules/min-dom": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/min-dom/-/min-dom-4.2.1.tgz", + "integrity": "sha512-TMoL8SEEIhUWYgkj7XMSgxmwSyGI+4fP2KFFGnN3FbHfbGHVdsLYSz8LoIsgPhz4dWRmLvxWWSMgzZMJW5sZuA==", + "dependencies": { + "component-event": "^0.2.1", + "domify": "^1.4.1", + "min-dash": "^4.2.1" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "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" + } + ], + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/preact": { + "version": "10.26.6", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.26.6.tgz", + "integrity": "sha512-5SRRBinwpwkaD+OqlBDeITlRgvd8I8QlxHJw9AxSdMNV6O+LodN9nUyYGpSF7sadHjs6RzeFShMexC6DbtWr9g==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/rollup": { + "version": "4.40.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.2.tgz", + "integrity": "sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.40.2", + "@rollup/rollup-android-arm64": "4.40.2", + "@rollup/rollup-darwin-arm64": "4.40.2", + "@rollup/rollup-darwin-x64": "4.40.2", + "@rollup/rollup-freebsd-arm64": "4.40.2", + "@rollup/rollup-freebsd-x64": "4.40.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.2", + "@rollup/rollup-linux-arm-musleabihf": "4.40.2", + "@rollup/rollup-linux-arm64-gnu": "4.40.2", + "@rollup/rollup-linux-arm64-musl": "4.40.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.2", + "@rollup/rollup-linux-riscv64-gnu": "4.40.2", + "@rollup/rollup-linux-riscv64-musl": "4.40.2", + "@rollup/rollup-linux-s390x-gnu": "4.40.2", + "@rollup/rollup-linux-x64-gnu": "4.40.2", + "@rollup/rollup-linux-x64-musl": "4.40.2", + "@rollup/rollup-win32-arm64-msvc": "4.40.2", + "@rollup/rollup-win32-ia32-msvc": "4.40.2", + "@rollup/rollup-win32-x64-msvc": "4.40.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/seroval": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.1.tgz", + "integrity": "sha512-F+T9EQPdLzgdewgxnBh4mSc+vde+EOkU6dC9BDuu/bfGb+UyUlqM6t8znFCTPQSuai/ZcfFg0gu79h+bVW2O0w==", + "engines": { + "node": ">=10" + } + }, + "node_modules/seroval-plugins": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.3.1.tgz", + "integrity": "sha512-dOlUoiI3fgZbQIcj6By+l865pzeWdP3XCSLdI3xlKnjCk5983yLWPsXytFOUI0BUZKG9qwqbj78n9yVcVwUqaQ==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "seroval": "^1.0" + } + }, + "node_modules/solid-js": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.7.tgz", + "integrity": "sha512-/saTKi8iWEM233n5OSi1YHCCuh66ZIQ7aK2hsToPe4tqGm7qAejU1SwNuTPivbWAYq7SjuHVVYxxuZQNRbICiw==", + "dependencies": { + "csstype": "^3.1.0", + "seroval": "~1.3.0", + "seroval-plugins": "~1.3.0" + } + }, + "node_modules/solid-refresh": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/solid-refresh/-/solid-refresh-0.6.3.tgz", + "integrity": "sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==", + "dev": true, + "dependencies": { + "@babel/generator": "^7.23.6", + "@babel/helper-module-imports": "^7.22.15", + "@babel/types": "^7.23.6" + }, + "peerDependencies": { + "solid-js": "^1.3" + } + }, + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/style-mod": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==" + }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" + }, + "node_modules/tailwind-merge": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.0.tgz", + "integrity": "sha512-fyW/pEfcQSiigd5SNn0nApUOxx0zB/dm6UDU/rEwc2c3sX2smWUNbapHv+QRqLGVp9GWX3THIa7MUGPo+YkDzQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.7.tgz", + "integrity": "sha512-kr1o/ErIdNhTz8uzAYL7TpaUuzKIE6QPQ4qmSdxnoX/lo+5wmUHQA6h3L5yIqEImSRnAAURDirLu/BgiXGPAhg==" + }, + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "engines": { + "node": ">=18" + } + }, + "node_modules/ticky": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ticky/-/ticky-1.0.1.tgz", + "integrity": "sha512-RX35iq/D+lrsqhcPWIazM9ELkjOe30MSeoBHQHSsRwd1YuhJO5ui1K1/R0r7N3mFvbLBs33idw+eR6j+w6i/DA==" + }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "devOptional": true + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/validate-html-nesting": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/validate-html-nesting/-/validate-html-nesting-1.2.2.tgz", + "integrity": "sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==", + "dev": true + }, + "node_modules/vite": { + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "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-plugin-solid": { + "version": "2.11.6", + "resolved": "https://registry.npmjs.org/vite-plugin-solid/-/vite-plugin-solid-2.11.6.tgz", + "integrity": "sha512-Sl5CTqJTGyEeOsmdH6BOgalIZlwH3t4/y0RQuFLMGnvWMBvxb4+lq7x3BSiAw6etf0QexfNJW7HSOO/Qf7pigg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.3", + "@types/babel__core": "^7.20.4", + "babel-preset-solid": "^1.8.4", + "merge-anything": "^5.1.7", + "solid-refresh": "^0.6.3", + "vitefu": "^1.0.4" + }, + "peerDependencies": { + "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", + "solid-js": "^1.7.2", + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "@testing-library/jest-dom": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.6.tgz", + "integrity": "sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA==", + "dev": true, + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } +} diff --git a/screener-frontend/package.json b/screener-frontend/package.json new file mode 100644 index 0000000..0e06fe5 --- /dev/null +++ b/screener-frontend/package.json @@ -0,0 +1,28 @@ +{ + "name": "screener-frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@bpmn-io/form-js": "^1.15.2", + "@solidjs/router": "^0.15.3", + "@tailwindcss/vite": "^4.1.7", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lodash.debounce": "^4.0.8", + "solid-js": "^1.9.5", + "tailwind-merge": "^3.3.0", + "tailwindcss": "^4.1.7", + "tailwindcss-animate": "^1.0.7" + }, + "devDependencies": { + "@types/node": "^22.15.18", + "vite": "^6.3.5", + "vite-plugin-solid": "^2.11.6" + } +} diff --git a/screener-frontend/public/vite.svg b/screener-frontend/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/screener-frontend/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/screener-frontend/src/App.css b/screener-frontend/src/App.css new file mode 100644 index 0000000..e69de29 diff --git a/screener-frontend/src/App.jsx b/screener-frontend/src/App.jsx new file mode 100644 index 0000000..f9f51c4 --- /dev/null +++ b/screener-frontend/src/App.jsx @@ -0,0 +1,15 @@ +import { Router, Route } from "@solidjs/router"; +import "./App.css"; +import Home from "./Home"; +import Screener from "./Screener"; + +function App() { + return ( + + + + + ); +} + +export default App; diff --git a/screener-frontend/src/EligibilityResults.jsx b/screener-frontend/src/EligibilityResults.jsx new file mode 100644 index 0000000..8250ec9 --- /dev/null +++ b/screener-frontend/src/EligibilityResults.jsx @@ -0,0 +1,125 @@ +import { createSignal, Show, Index, Switch, Match } from "solid-js"; +import testData from "./testData.js"; +import checkIcon from "./assets/images/checkIcon.svg"; +import questionIcon from "./assets/images/questionIcon.svg"; +import xIcon from "./assets/images/xIcon.svg"; + +export default function EligibilityResults() { + const [testResults, setTestResults] = createSignal({}); + setTestResults(testData); + return ( +
+

Eligibility Results

+ + {(benefitName, index) => ( +
+ + +

+ Eligible +

+
+ +

+ Need more information +

+
+ +

+ Ineligible +

+
+
+

+ {testResults()["benefits"][benefitName()]["name"]} +

+
+ + {(checkName, index) => ( +

+ + + + + + + + + + + + + { + testResults()["benefits"][benefitName()]["eligibility"][ + "checks" + ][checkName()]["name"] + } + +

+ )} +
+
+

Overview

+

+ {testResults()["benefits"][benefitName()]["info"]} +

+ +

+ Apply Now +

+
+
+ )} +
+
+ ); +} diff --git a/screener-frontend/src/Error.jsx b/screener-frontend/src/Error.jsx new file mode 100644 index 0000000..f111138 --- /dev/null +++ b/screener-frontend/src/Error.jsx @@ -0,0 +1,10 @@ +export default function ErrorPage({ error }) { + console.log(error); + return ( +
+

+ Sorry, there was an error loading the requested screener. +

+
+ ); +} diff --git a/screener-frontend/src/FormRenderer.jsx b/screener-frontend/src/FormRenderer.jsx new file mode 100644 index 0000000..9cf484a --- /dev/null +++ b/screener-frontend/src/FormRenderer.jsx @@ -0,0 +1,34 @@ +import { onMount } from "solid-js"; +import { Form } from "@bpmn-io/form-js-viewer"; +import debounce from "lodash.debounce"; +import "@bpmn-io/form-js/dist/assets/form-js.css"; +import { useParams } from "@solidjs/router"; + +function FormRenderer({ schema, submitForm }) { + let container; + + onMount(() => { + const form = new Form({ container }); + + const debouncedSubmit = debounce((data) => { + submitForm(data); + }, 1000); + + form + .importSchema(schema) + .then(() => { + form.on("changed", (event) => { + debouncedSubmit(event.data); + }); + }) + .catch(console.error); + }); + + return ( +
+
(container = el)} /> +
+ ); +} + +export default FormRenderer; diff --git a/screener-frontend/src/Home.jsx b/screener-frontend/src/Home.jsx new file mode 100644 index 0000000..9df9457 --- /dev/null +++ b/screener-frontend/src/Home.jsx @@ -0,0 +1,7 @@ +export default function Home() { + return ( +
+

Benefit Decision Toolkit

+
+ ); +} diff --git a/screener-frontend/src/Loading.jsx b/screener-frontend/src/Loading.jsx new file mode 100644 index 0000000..f9df769 --- /dev/null +++ b/screener-frontend/src/Loading.jsx @@ -0,0 +1,10 @@ +export default function Loading() { + return ( +
+

Loading Screener

+
+
+
+
+ ); +} diff --git a/screener-frontend/src/NotFound.jsx b/screener-frontend/src/NotFound.jsx new file mode 100644 index 0000000..f476840 --- /dev/null +++ b/screener-frontend/src/NotFound.jsx @@ -0,0 +1,7 @@ +export default function NotFound() { + return ( +
+

404 Screener Not Found

+
+ ); +} diff --git a/screener-frontend/src/Results.jsx b/screener-frontend/src/Results.jsx new file mode 100644 index 0000000..f500c60 --- /dev/null +++ b/screener-frontend/src/Results.jsx @@ -0,0 +1,10 @@ +import { For } from "solid-js"; +import EligibilityResults from "./EligibilityResults"; + +export default function Results({ results }) { + return ( +
+ +
+ ); +} diff --git a/screener-frontend/src/Screener.jsx b/screener-frontend/src/Screener.jsx new file mode 100644 index 0000000..c57b8fd --- /dev/null +++ b/screener-frontend/src/Screener.jsx @@ -0,0 +1,48 @@ +import { createSignal, createResource, ErrorBoundary } from "solid-js"; +import { useParams } from "@solidjs/router"; +import FormRenderer from "./FormRenderer"; +import Results from "./Results"; +import { fetchScreenerData, getDecisionResult } from "./api/api"; +import Loading from "./Loading"; +import ErrorPage from "./Error"; +import testData from "./testData"; + +export default function Screener() { + const params = useParams(); + + const [data] = createResource(() => fetchScreenerData(params.screenerId)); + const [results, setResults] = createSignal(); + + const submitForm = async (data) => { + try { + let results = await getDecisionResult(params.screenerId, data); + setResults(results); + console.log(results); + } catch (err) { + console.log(err); + } + }; + + return ( + <> +
+ } + > + {data.loading && } + {data() && ( + <> + +
+ +
+ + )} +
+
+ + ); +} diff --git a/screener-frontend/src/api/api.js b/screener-frontend/src/api/api.js new file mode 100644 index 0000000..4152980 --- /dev/null +++ b/screener-frontend/src/api/api.js @@ -0,0 +1,39 @@ +const BASE_URL = import.meta.env.VITE_API_URL; + +export const fetchScreenerData = async (screenerId) => { + try { + const response = await fetch(`${BASE_URL}screener/${screenerId}`); + if (!response.ok) { + throw new Error(`Error: ${response.status}`); + } + const data = await response.json(); + return data; + } catch (error) { + console.error("Error: failed to fetch screener form data", error); + throw error; + } +}; + +export const getDecisionResult = async (screenerId, data) => { + try { + const response = await fetch( + `${BASE_URL}decision?screenerId=${screenerId}`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + } + ); + + if (!response.ok) { + throw new Error("Failed to submit form"); + } + const result = await response.json(); + return result; + } catch (err) { + console.log("Error submitting form"); + console.log(err); + } +}; diff --git a/screener-frontend/src/assets/images/checkIcon.svg b/screener-frontend/src/assets/images/checkIcon.svg new file mode 100644 index 0000000..72db4b1 --- /dev/null +++ b/screener-frontend/src/assets/images/checkIcon.svg @@ -0,0 +1,17 @@ + + + + diff --git a/screener-frontend/src/assets/images/questionIcon.svg b/screener-frontend/src/assets/images/questionIcon.svg new file mode 100644 index 0000000..694cc2f --- /dev/null +++ b/screener-frontend/src/assets/images/questionIcon.svg @@ -0,0 +1,17 @@ + + + + diff --git a/screener-frontend/src/assets/images/xIcon.svg b/screener-frontend/src/assets/images/xIcon.svg new file mode 100644 index 0000000..994d3c3 --- /dev/null +++ b/screener-frontend/src/assets/images/xIcon.svg @@ -0,0 +1,17 @@ + + + + diff --git a/screener-frontend/src/assets/solid.svg b/screener-frontend/src/assets/solid.svg new file mode 100644 index 0000000..025aa30 --- /dev/null +++ b/screener-frontend/src/assets/solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/screener-frontend/src/index.css b/screener-frontend/src/index.css new file mode 100644 index 0000000..1f9799c --- /dev/null +++ b/screener-frontend/src/index.css @@ -0,0 +1,13 @@ +@import "tailwindcss"; + +.fjs-powered-by { + display: none; +} + +body { + font-family: "IBM Plex Sans", sans-serif; +} + +h1 { + font-size: 2em; +} diff --git a/screener-frontend/src/index.jsx b/screener-frontend/src/index.jsx new file mode 100644 index 0000000..a80d6d7 --- /dev/null +++ b/screener-frontend/src/index.jsx @@ -0,0 +1,8 @@ +/* @refresh reload */ +import { render } from 'solid-js/web' +import './index.css' +import App from './App.jsx' + +const root = document.getElementById('root') + +render(() => , root) diff --git a/screener-frontend/src/testData.js b/screener-frontend/src/testData.js new file mode 100644 index 0000000..e304d81 --- /dev/null +++ b/screener-frontend/src/testData.js @@ -0,0 +1,77 @@ +export default { + benefits: { + phlLoop: { + name: "Longtime Owner Occupants Program (LOOP)", + eligibility: { + result: true, + checks: { + tenOrMoreYearsOwnerOccupant: { + name: "Occupant for ten years or more", + result: true, + }, + notEnrolledInPhlLoop: { + name: "Not enrolled in LOOP", + result: true, + }, + noTenYearTaxAbatement: { + name: "No ten-year tax abatement", + result: true, + }, + }, + }, + info: "The Longtime Owner Occupants Program (LOOP) is a Real Estate Tax relief program for eligible homeowners whose property assessments increased by at least 50% from last year or increased by at least 75% in the last five years.", + appLink: + "https://www.phila.gov/documents/longtime-owner-occupants-program-loop-forms/", + }, + phlSeniorCitizenTaxFreeze: { + name: "Senior Citizen Tax Freeze", + eligibility: { + result: null, + checks: { + notEnrolledInPhlSeniorCitizenTaxFreeze: { + name: "Not enrolled in senior citizen tax freeze", + result: true, + }, + meetsPhlSeniorCitizenTaxFreezeAgeRequirements: { + name: "Meets senior citizen tax freeze requirements", + result: null, + }, + phillyOwnerOccupantHomeowner: { + name: "Owns a home in Philadelphia", + result: true, + }, + }, + }, + info: "Quo qui minus nisi facilis dolorem ullam ex. Perspiciatis expedita doloremque ratione esse non iusto. Eos qui dolore quisquam ipsum vel. Dolores dignissimos quibusdam qui tempora et autem dolore. Accusantium saepe praesentium et dignissimos veniam perspiciatis magni quam.", + appLink: + "https://www.phila.gov/documents/longtime-owner-occupants-program-loop-forms/", + }, + phlLowIncomeTaxFreeze: { + name: "Low Income Tax Freeze", + eligibility: { + result: false, + checks: { + phillyOwnerOccupantHomeowner: { + name: "Owns a home in Philadelphia", + result: false, + }, + notEnrolledInPhlLowIncomeTaxFreeze: { + name: "Not enrolled in low income tax freeze", + result: null, + }, + underMaritalStatusBasedCurrentMonthlyGrossIncome: { + name: "Under marital status-based monthly gross income", + result: true, + }, + }, + }, + info: "Quo qui minus nisi facilis dolorem ullam ex. Perspiciatis expedita doloremque ratione esse non iusto. Eos qui dolore quisquam ipsum vel. Dolores dignissimos quibusdam qui tempora et autem dolore. Accusantium saepe praesentium et dignissimos veniam perspiciatis magni quam.", + appLink: + "https://www.phila.gov/documents/longtime-owner-occupants-program-loop-forms/", + }, + }, + branding: { + orgName: "Organization Name", + authorInfo: "Created by Org Name", + }, +}; diff --git a/screener-frontend/tailwind.config.js b/screener-frontend/tailwind.config.js new file mode 100644 index 0000000..1cf4299 --- /dev/null +++ b/screener-frontend/tailwind.config.js @@ -0,0 +1,6 @@ +/**@type {import("tailwindcss").Config} */ +module.exports = { + darkMode: ["variant", [".dark &", '[data-kb-theme="dark"] &']], + content: ["./src/**/*.{ts,tsx}"], + plugins: [require("tailwindcss-animate")], +}; diff --git a/screener-frontend/vite.config.js b/screener-frontend/vite.config.js new file mode 100644 index 0000000..c65931b --- /dev/null +++ b/screener-frontend/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from "vite"; +import solid from "vite-plugin-solid"; +import tailwindcss from "@tailwindcss/vite"; + +export default defineConfig({ + plugins: [solid(), tailwindcss()], +});