diff --git a/.gitignore b/.gitignore index 5fe485a1..9f9dafaf 100644 --- a/.gitignore +++ b/.gitignore @@ -2,9 +2,12 @@ elm-stuff/ node_modules/ +out/ -.metals -.bloop +.metals/ +.bloop/ +.bsp/ +.scala-build/ .vscode .gitattributes @@ -12,3 +15,7 @@ node_modules/ morphir-hashes.json morphir-ir.json package-lock.json +*.semanticdb + +.user/ +.out/ \ No newline at end of file diff --git a/.mill-version b/.mill-version new file mode 100644 index 00000000..767eab02 --- /dev/null +++ b/.mill-version @@ -0,0 +1 @@ +0.12.0-RC2 diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 00000000..2591428d --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,10 @@ +version = "3.7.15" +runner.dialect = scala213 +fileOverride { + "glob:**/api/src/**" { + runner.dialect = scala3 + } + "glob:**/serverless/src/**" { + runner.dialect = scala3 + } +} diff --git a/API/build.gradle.kts b/API/build.gradle.kts deleted file mode 100644 index 9d16e095..00000000 --- a/API/build.gradle.kts +++ /dev/null @@ -1,32 +0,0 @@ -plugins { - id("scala") -} - -group = "bankerX" -version = "1.0-SNAPSHOT" -java.sourceCompatibility = JavaVersion.VERSION_21 - -repositories { - mavenCentral() -} - -dependencies { - implementation("org.scala-lang:scala-library:2.13.14") - // testImplementation("org.scalatest:scalatest_2.13:3.2.10") - // testRuntimeOnly("org.junit.platform:junit-platform-launcher") -} - -// tasks { -// withType { -// useJUnitPlatform() -// } -// } - -sourceSets { - main { - scala.srcDirs("src/main/scala", "build/generated/sources/scala") - } - test { - scala.srcDirs("src/test/scala") - } -} \ No newline at end of file diff --git a/API/elm.json b/API/elm.json deleted file mode 100644 index d20defbb..00000000 --- a/API/elm.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "type": "application", - "source-directories": [ - "src" - ], - "elm-version": "0.19.1", - "dependencies": { - "direct": { - "elm/browser": "1.0.2", - "elm/core": "1.0.5", - "elm/html": "1.0.0", - "finos/morphir-elm": "22.0.0" - }, - "indirect": { - "TSFoster/elm-bytes-extra": "1.3.0", - "TSFoster/elm-md5": "2.0.1", - "TSFoster/elm-sha1": "2.1.1", - "TSFoster/elm-uuid": "4.2.0", - "avh4/elm-color": "1.0.0", - "avh4/elm-fifo": "1.0.4", - "chain-partners/elm-bignum": "1.0.1", - "cmditch/elm-bigint": "1.0.1", - "cuducos/elm-format-number": "8.1.4", - "danfishgold/base64-bytes": "1.1.0", - "dillonkearns/elm-markdown": "7.0.1", - "dosarf/elm-tree-view": "3.0.0", - "elm/bytes": "1.0.8", - "elm/json": "1.1.3", - "elm/parser": "1.1.0", - "elm/random": "1.0.0", - "elm/regex": "1.0.0", - "elm/svg": "1.0.1", - "elm/time": "1.0.0", - "elm/url": "1.0.0", - "elm/virtual-dom": "1.0.3", - "elm-community/graph": "6.0.0", - "elm-community/intdict": "3.0.0", - "elm-community/list-extra": "8.7.0", - "elm-community/maybe-extra": "5.3.0", - "elm-community/random-extra": "3.2.0", - "elm-explorations/markdown": "1.0.0", - "elm-explorations/test": "2.2.0", - "erlandsona/assoc-set": "1.1.3", - "fabhof/elm-ui-datepicker": "5.0.0", - "justinmimbs/date": "3.2.1", - "lattyware/elm-fontawesome": "6.0.0", - "matthewsj/elm-ordering": "2.0.0", - "mdgriffith/elm-ui": "1.1.8", - "miniBill/elm-unicode": "1.1.1", - "myrho/elm-round": "1.0.5", - "pzp1997/assoc-list": "1.0.0", - "rtfeldman/elm-hex": "1.0.0", - "rtfeldman/elm-iso8601-date-strings": "1.1.4", - "rundis/elm-bootstrap": "5.2.0", - "stil4m/elm-syntax": "7.3.4", - "stil4m/structured-writer": "1.0.3" - } - }, - "test-dependencies": { - "direct": {}, - "indirect": {} - } -} diff --git a/API/gradle/libs.versions.toml b/API/gradle/libs.versions.toml deleted file mode 100644 index 4ac3234a..00000000 --- a/API/gradle/libs.versions.toml +++ /dev/null @@ -1,2 +0,0 @@ -# This file was generated by the Gradle 'init' task. -# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format diff --git a/API/gradle/wrapper/gradle-wrapper.jar b/API/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 2c352119..00000000 Binary files a/API/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/API/gradle/wrapper/gradle-wrapper.properties b/API/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 09523c0e..00000000 --- a/API/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/API/gradlew b/API/gradlew deleted file mode 100755 index f5feea6d..00000000 --- a/API/gradlew +++ /dev/null @@ -1,252 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/API/gradlew.bat b/API/gradlew.bat deleted file mode 100644 index 9d21a218..00000000 --- a/API/gradlew.bat +++ /dev/null @@ -1,94 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/API/morphir-hashes.json b/API/morphir-hashes.json deleted file mode 100644 index f0c61fa9..00000000 --- a/API/morphir-hashes.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "src/BankerX/API.elm": "31bb81d4ad0b0d3ee38d416b95d5ddcc" -} \ No newline at end of file diff --git a/API/morphir.json b/API/morphir.json deleted file mode 100644 index da8c6512..00000000 --- a/API/morphir.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "BankerX", - "sourceDirectory": "src" - } \ No newline at end of file diff --git a/API/package-lock.json b/API/package-lock.json deleted file mode 100644 index d32789e6..00000000 --- a/API/package-lock.json +++ /dev/null @@ -1,1507 +0,0 @@ -{ - "name": "api", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "api", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "morphir-elm": "^2.89.0" - } - }, - "node_modules/@egjs/hammerjs": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", - "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==", - "peer": true, - "dependencies": { - "@types/hammerjs": "^2.0.36" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@types/hammerjs": { - "version": "2.0.45", - "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.45.tgz", - "integrity": "sha512-qkcUlZmX6c4J8q45taBKTL3p+LbITgyx7qhlPYOdOHZB7B31K0mXbP5YA7i7SgDeEGuI9MnumiKPEMrxg8j3KQ==", - "peer": true - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "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/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "engines": { - "node": ">=0.8" - } - }, - "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/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "engines": { - "node": "^12.20.0 || >=14" - } - }, - "node_modules/component-emitter": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", - "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-parser": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", - "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", - "dependencies": { - "cookie": "0.4.1", - "cookie-signature": "1.0.6" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "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/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "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/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/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-uri": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", - "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "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/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "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==" - }, - "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-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "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/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "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/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/inquirer": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", - "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^6.0.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "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-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/keycharm": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.4.0.tgz", - "integrity": "sha512-TyQTtsabOVv3MeOpR92sIKk/br9wxS+zGj4BG7CR8YbK4jM3tyIBaF0zhzeBUMx36/Q/iQLOKKOT+3jOQtemRQ==", - "peer": true - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/log-prefix": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/log-prefix/-/log-prefix-0.1.1.tgz", - "integrity": "sha512-aP1Lst8OCdZKATqzXDN0JBissNVZuiKLyo6hOXDBxaQ1jHDsaxh2J1i5Pp0zMy6ayTKDWfUlLMXyLaQe1PJ48g==", - "engines": { - "node": "*" - } - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-timestamp": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/log-timestamp/-/log-timestamp-0.3.0.tgz", - "integrity": "sha512-luRz6soxijd1aJh0GkLXFjKABihxthvTfWTzu3XhCgg5EivG2bsTpSd63QFbUgS+/KmFtL+0RfSpeaD2QvOV8Q==", - "dependencies": { - "log-prefix": "0.1.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "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/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/morphir-elm": { - "version": "2.89.0", - "resolved": "https://registry.npmjs.org/morphir-elm/-/morphir-elm-2.89.0.tgz", - "integrity": "sha512-ZXvRC4YvGrbYhaC/rKbJ2wvqEN3RpjbRMuAozpvlSWA+dsuAPoK5rNEtG3Wj1zgBqVtUsE9Jo6G6vsQRVpoa1A==", - "dependencies": { - "ajv": "^8.10.0", - "ajv-formats": "^2.1.1", - "chalk": "^4.1.1", - "commander": "^9.0.0", - "cookie-parser": "^1.4.6", - "express": "^4.18.2", - "fs-extra": "^9.1.0", - "get-stdin": "^8.0.0", - "inquirer": "^8.0.0", - "log-timestamp": "^0.3.0", - "prettier": "^2.4.1", - "vis-network": "^9.1.2" - }, - "bin": { - "morphir": "cli2/lib/morphir.js", - "morphir-dapr": "cli/morphir-dapr.js", - "morphir-elm": "cli/morphir-elm.js" - }, - "engines": { - "node": "*", - "npm": "*" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "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/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/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/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.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/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/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "peer": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vis-data": { - "version": "7.1.9", - "resolved": "https://registry.npmjs.org/vis-data/-/vis-data-7.1.9.tgz", - "integrity": "sha512-COQsxlVrmcRIbZMMTYwD+C2bxYCFDNQ2EHESklPiInbD/Pk3JZ6qNL84Bp9wWjYjAzXfSlsNaFtRk+hO9yBPWA==", - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/visjs" - }, - "peerDependencies": { - "uuid": "^3.4.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", - "vis-util": "^5.0.1" - } - }, - "node_modules/vis-network": { - "version": "9.1.9", - "resolved": "https://registry.npmjs.org/vis-network/-/vis-network-9.1.9.tgz", - "integrity": "sha512-Ft+hLBVyiLstVYSb69Q1OIQeh3FeUxHJn0WdFcq+BFPqs+Vq1ibMi2sb//cxgq1CP7PH4yOXnHxEH/B2VzpZYA==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/visjs" - }, - "peerDependencies": { - "@egjs/hammerjs": "^2.0.0", - "component-emitter": "^1.3.0", - "keycharm": "^0.2.0 || ^0.3.0 || ^0.4.0", - "uuid": "^3.4.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", - "vis-data": "^6.3.0 || ^7.0.0", - "vis-util": "^5.0.1" - } - }, - "node_modules/vis-util": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/vis-util/-/vis-util-5.0.7.tgz", - "integrity": "sha512-E3L03G3+trvc/X4LXvBfih3YIHcKS2WrP0XTdZefr6W6Qi/2nNCqZfe4JFfJU6DcQLm6Gxqj2Pfl+02859oL5A==", - "peer": true, - "engines": { - "node": ">=8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/visjs" - }, - "peerDependencies": { - "@egjs/hammerjs": "^2.0.0", - "component-emitter": "^1.3.0 || ^2.0.0" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - } - } -} diff --git a/API/settings.gradle.kts b/API/settings.gradle.kts deleted file mode 100644 index 8d2e8a17..00000000 --- a/API/settings.gradle.kts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * The settings file is used to specify which projects to include in your build. - * For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.9/userguide/multi_project_builds.html in the Gradle documentation. - */ - -rootProject.name = "bankerX.API" diff --git a/README.md b/README.md index 992ce150..88e5154c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # BankerX -Morphir is a standard language for business logic, FDC3 is a standard language for application interoperability. BankerX is a reference example combining the FDC3 and Morphir projects so that a web application can use the FDC3 protocol to integrate with Morphir based services by speaking FDC3 over REST. +Morphir is a standard language for business logic, FDC3 is a standard language for application interoperability. BankerX is a reference example combining the FDC3 and Morphir projects so that a web application can use the FDC3 protocol to integrate with Morphir based services by speaking FDC3 over REST. ## Installation @@ -15,7 +15,7 @@ run the local application... ## Usage example -This project can be used as a reference example for specific FDC3 and Morphir data flows. It can also be used as an example of novel FDC3 and Morphir use cases. In this case, commercial banking, to expand the conception of business value for both projects. +This project can be used as a reference example for specific FDC3 and Morphir data flows. It can also be used as an example of novel FDC3 and Morphir use cases. In this case, commercial banking, to expand the conception of business value for both projects. ## Development setup @@ -66,14 +66,26 @@ interface Purchase { } } } + +//example + { + type: 'fdc3.purchase', + amount: 30, + vendor: 'My Favorite Vendor', + timestamp: new Date().getDate(), + purchaser: 'me', + merchant: 'you', + category: 'stuff' +} + ``` TermsList ('fdc3.termsList') ```ts interface TermsList { - type: string; //'fdc3.termsList' - terms: [Terms]; + type: string; //'fdc3.termsList' + terms: [Terms]; } ``` @@ -82,6 +94,7 @@ Terms ('fdc3.Terms') ```ts interface Terms { type: string; //'fdc3.terms + data: { points: number; rate: number; @@ -104,9 +117,26 @@ interface Terms { } } + ``` -intent: MakePurchase +intent: MakePurchase (result) + +```ts + interface PurchaseConfirmation { + type: string; //fdc3.purchaseConfirmation + provider: Provider; + } + + //example + { + type: 'fdc3.purchaseConfirmation', + provider: { + name: 'E*TRADE', + id: 'testApp1', + logo: './images/etrade.png' + } + ```ts @@ -143,6 +173,35 @@ List the roadmap steps; alternatively link the Confluence Wiki page where the pr 2. Item 2 3. .... +## Developing + +The project uses Morphir and FDC3. + +Server related code is done with: + +- [Tapir](https://tapir.softwaremill.com/) +- [jsoniter-scala](https://github.com/plokhotnyuk/jsoniter-scala) +- AWS Lambda +- The CDK for deployment + +Run the server locally: + +Compile everything: + +```shell +./mill __.compile +``` + +```shell +./mill server.run +``` + +Run the serverless backend with sam local: + +```shell +./mill cdk.runSamLocal +``` + ## Contributing 1. Fork it () diff --git a/API/.gitignore b/api/.gitignore similarity index 82% rename from API/.gitignore rename to api/.gitignore index 15d011a7..6e33bdba 100644 --- a/API/.gitignore +++ b/api/.gitignore @@ -4,4 +4,5 @@ # Ignore Gradle build output directory build dist -elm-stuff \ No newline at end of file +elm-stuff +src/generated \ No newline at end of file diff --git a/api/ReadMe.md b/api/ReadMe.md new file mode 100644 index 00000000..8c19696c --- /dev/null +++ b/api/ReadMe.md @@ -0,0 +1,50 @@ +# BankerX API + +Contains the Morphir model for the BankerX project. + +The model includes the API for the simple banking application, that is setup to demonstrate the use of the FDC3 protocol to integrate with Morphir based services by speaking FDC3 over REST. + +## Developing + +### Building + +At the root of the project run the following command to build the api project: + +```sh +./mill api.build +``` + +
+ +Building for Windows users + +```sh +mill.bat api.build +``` + +or using Powershell + +```sh +.\mill.ps1 api.build +``` + +
+ +### Testing + +At the root of the project run the following command to test the api project: + +```sh +./mill api.test +``` + +### Morphir Code Generation + +The custom mill target called `morphirScalaGen` found in the `MorphirScalaModule` is responsible for generating the Scala code from the Morphir model. +You can find the various reused custom mill targets in [`util.mill`](../util.mill) at the root of the project. + +> NOTE: In order to keep incremental compilation running smoothly, the code is generated into the mill out folder, as according to mill's conventions. +> The generated [morphir-ir.json](../out/api/morphirMakeOutputDir.dest/morphir-ir.json) is found in the out folder if the `api.build` target is ran. +> The [generated Scala code](../out/api/morphirScalaGenOutputDir.dest/) is found in the out folder if the `morphirScalaGen` target is ran (it is automatically ran as a result of running `build`). + +Part of the benefit of using mill is its ability to heavily customize the build process using normal Scala code. diff --git a/api/elm.json b/api/elm.json new file mode 100644 index 00000000..b3195f6f --- /dev/null +++ b/api/elm.json @@ -0,0 +1,61 @@ +{ + "type": "application", + "source-directories": ["src-elm"], + "elm-version": "0.19.1", + "dependencies": { + "direct": { + "elm/browser": "1.0.2", + "elm/core": "1.0.5", + "elm/html": "1.0.0", + "finos/morphir-elm": "22.0.0" + }, + "indirect": { + "TSFoster/elm-bytes-extra": "1.3.0", + "TSFoster/elm-md5": "2.0.1", + "TSFoster/elm-sha1": "2.1.1", + "TSFoster/elm-uuid": "4.2.0", + "avh4/elm-color": "1.0.0", + "avh4/elm-fifo": "1.0.4", + "chain-partners/elm-bignum": "1.0.1", + "cmditch/elm-bigint": "1.0.1", + "cuducos/elm-format-number": "8.1.4", + "danfishgold/base64-bytes": "1.1.0", + "dillonkearns/elm-markdown": "7.0.1", + "dosarf/elm-tree-view": "3.0.0", + "elm/bytes": "1.0.8", + "elm/json": "1.1.3", + "elm/parser": "1.1.0", + "elm/random": "1.0.0", + "elm/regex": "1.0.0", + "elm/svg": "1.0.1", + "elm/time": "1.0.0", + "elm/url": "1.0.0", + "elm/virtual-dom": "1.0.3", + "elm-community/graph": "6.0.0", + "elm-community/intdict": "3.0.0", + "elm-community/list-extra": "8.7.0", + "elm-community/maybe-extra": "5.3.0", + "elm-community/random-extra": "3.2.0", + "elm-explorations/markdown": "1.0.0", + "elm-explorations/test": "2.2.0", + "erlandsona/assoc-set": "1.1.3", + "fabhof/elm-ui-datepicker": "5.0.0", + "justinmimbs/date": "3.2.1", + "lattyware/elm-fontawesome": "6.0.0", + "matthewsj/elm-ordering": "2.0.0", + "mdgriffith/elm-ui": "1.1.8", + "miniBill/elm-unicode": "1.1.1", + "myrho/elm-round": "1.0.5", + "pzp1997/assoc-list": "1.0.0", + "rtfeldman/elm-hex": "1.0.0", + "rtfeldman/elm-iso8601-date-strings": "1.1.4", + "rundis/elm-bootstrap": "5.2.0", + "stil4m/elm-syntax": "7.3.4", + "stil4m/structured-writer": "1.0.3" + } + }, + "test-dependencies": { + "direct": {}, + "indirect": {} + } +} diff --git a/api/morphir.json b/api/morphir.json new file mode 100644 index 00000000..0e416afa --- /dev/null +++ b/api/morphir.json @@ -0,0 +1,4 @@ +{ + "name": "BankerX", + "sourceDirectory": "src-elm" +} diff --git a/API/package.json b/api/package.json similarity index 68% rename from API/package.json rename to api/package.json index 3d7b9f49..b516b38a 100644 --- a/API/package.json +++ b/api/package.json @@ -4,10 +4,11 @@ "description": "BankerX API definitions", "main": "index.js", "scripts": { + "build": "morphir-elm gen -c -s --target-version 3.5.0 --output src/generated/scala", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", - "license": "ISC", + "license": "Apache-2.0", "dependencies": { "morphir-elm": "^2.89.0" } diff --git a/api/package.mill b/api/package.mill new file mode 100644 index 00000000..7b8c00c6 --- /dev/null +++ b/api/package.mill @@ -0,0 +1,23 @@ +package build.api +import mill._, scalalib._ +import $file.versions.V +import $file.util._ + +object `package` extends RootModule with MorphirScalaModule with BankerXScalaModule { + //def moduleDeps = Seq(build.bar.qux.mymodule) + def ivyDeps = Agg( + ivy"org.morphir::morphir-sdk-core:${V.Libs.`morphir-jvm`}", + ivy"org.morphir::morphir-ir:${V.Libs.`morphir-jvm`}", + ivy"com.softwaremill.sttp.tapir::tapir-core::${V.Libs.tapir}", + ivy"com.softwaremill.sttp.tapir::tapir-jsoniter-scala::${V.Libs.tapir}", + ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-core:${V.Libs.`jsoniter-scala`}", + ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:${V.Libs.`jsoniter-scala`}", + ) + + object test extends ScalaTests with TestModule.ScalaTest{ + def ivyDeps = Agg( + ivy"org.scalatest::scalatest::${V.Libs.scalatest}", + ivy"com.lihaoyi::pprint::${V.Libs.pprint}" + ) + } +} \ No newline at end of file diff --git a/API/src/BankerX/API.elm b/api/src-elm/BankerX/API.elm similarity index 84% rename from API/src/BankerX/API.elm rename to api/src-elm/BankerX/API.elm index ed323c5d..57588a11 100644 --- a/API/src/BankerX/API.elm +++ b/api/src-elm/BankerX/API.elm @@ -16,7 +16,8 @@ type alias Terms = , promotionalPeriod : PromotionalPeriod } - +type alias BankName = String +type alias BankID = String type alias Amount = Int type alias Vendor = String type alias Date = LocalDate @@ -41,8 +42,11 @@ type alias Purchase = , pointOfSale : PointOfSale } +type alias BankRegistration = + { bankName : BankName + , bankID : BankID + , getTerms : Purchase -> Terms + } -- getTerms : Purchase -> Terms -- getTerms purchase = todo "Implement getTerms" - - diff --git a/api/src-elm/BankerX/Banks/CapitalOne.elm b/api/src-elm/BankerX/Banks/CapitalOne.elm new file mode 100644 index 00000000..5edc5a10 --- /dev/null +++ b/api/src-elm/BankerX/Banks/CapitalOne.elm @@ -0,0 +1,41 @@ +module BankerX.Banks.CapitalOne exposing (..) + +import BankerX.API exposing (..) +import Morphir.SDK.LocalDate exposing (LocalDate) +import Morphir.SDK.LocalTime exposing (LocalTime) + +bankname = "Capital One Bank" +bankId = "CapitalOne" +registration: BankRegistration +registration = + { bankName = bankname + , bankID = bankId + , getTerms = getTerms + } + +preferredVendors : List Vendor +preferredVendors = + [ "Vendor.A" + , "Vendor.W" + , "Vendor.T" + ] + +getTerms : Purchase -> Terms +getTerms purchase = + let + points : Points + points = + getPoints purchase.vendor purchase.amount + in + { provider = bankname + , points = points + , interestRate = 0.5 + , promotionalPeriod = 30 + } + +getPoints : Vendor -> Amount -> Points +getPoints vendor amount = + if List.member vendor preferredVendors then + 4 * amount + else + amount diff --git a/api/src-elm/BankerX/Banks/Etrade.elm b/api/src-elm/BankerX/Banks/Etrade.elm new file mode 100644 index 00000000..3c2230e4 --- /dev/null +++ b/api/src-elm/BankerX/Banks/Etrade.elm @@ -0,0 +1,41 @@ +module BankerX.Banks.Etrade exposing (..) + +import BankerX.API exposing (..) +import Morphir.SDK.LocalDate exposing (LocalDate) +import Morphir.SDK.LocalTime exposing (LocalTime) + +bankname = "Etrade" +bankId = "Etrade" +registration: BankRegistration +registration = + { bankName = bankname + , bankID = bankId + , getTerms = getTerms + } + +preferredVendors : List Vendor +preferredVendors = + [ "Gamestop" + , "Morgan Stanley" + , "NYSE" + ] + +getTerms : Purchase -> Terms +getTerms purchase = + let + points : Points + points = + getPoints purchase.vendor purchase.amount + in + { provider = bankname + , points = points + , interestRate = 0.615 + , promotionalPeriod = 60 + } + +getPoints : Vendor -> Amount -> Points +getPoints vendor amount = + if List.member vendor preferredVendors then + 4 * amount + else + amount diff --git a/API/src/BankerX/FirstBank.elm b/api/src-elm/BankerX/Banks/FirstBank.elm similarity index 93% rename from API/src/BankerX/FirstBank.elm rename to api/src-elm/BankerX/Banks/FirstBank.elm index 0e87a7b2..08a23709 100644 --- a/API/src/BankerX/FirstBank.elm +++ b/api/src-elm/BankerX/Banks/FirstBank.elm @@ -1,4 +1,4 @@ -module BankerX.FirstBank exposing (..) +module BankerX.Banks.FirstBank exposing (..) import BankerX.API exposing (..) import Morphir.SDK.LocalDate exposing (LocalDate) diff --git a/API/src/BankerX/SecondBank.elm b/api/src-elm/BankerX/Banks/SecondBank.elm similarity index 93% rename from API/src/BankerX/SecondBank.elm rename to api/src-elm/BankerX/Banks/SecondBank.elm index 8cb333fa..ada20962 100644 --- a/API/src/BankerX/SecondBank.elm +++ b/api/src-elm/BankerX/Banks/SecondBank.elm @@ -1,4 +1,4 @@ -module BankerX.SecondBank exposing (..) +module BankerX.Banks.SecondBank exposing (..) import BankerX.API exposing (..) import Morphir.SDK.LocalDate exposing (LocalDate) diff --git a/api/src-elm/BankerX/SmartWallet.elm b/api/src-elm/BankerX/SmartWallet.elm new file mode 100644 index 00000000..3ded9f1d --- /dev/null +++ b/api/src-elm/BankerX/SmartWallet.elm @@ -0,0 +1,26 @@ +module BankerX.SmartWallet exposing (..) +import BankerX.API exposing (..) + +import BankerX.Banks.CapitalOne as CapitalOne +import BankerX.Banks.Etrade as Etrade +import BankerX.Banks.FirstBank as FirstBank +import BankerX.Banks.SecondBank as SecondBank + +{-| Service definition for the SmartWallet service -} +type alias Service = + { getTerms: BankName -> Purchase -> Maybe Terms} + +{-| Service implementation for the SmartWallet service -} +service : Service +service = + { getTerms = getTerms } + +{-| Get the terms for a purchase from a bank -} +getTerms : BankName -> Purchase -> Maybe Terms +getTerms bankName purchase = + case bankName of + "CapitalOne" -> Just(CapitalOne.getTerms purchase) + "Etrade" -> Just(Etrade.getTerms purchase) + "FirstBank" -> Just(FirstBank.getTerms purchase) + "SecondBank" -> Just(SecondBank.getTerms purchase) + _ -> Nothing \ No newline at end of file diff --git a/api/src/bankerx/api/Codecs.scala b/api/src/bankerx/api/Codecs.scala new file mode 100644 index 00000000..c635f252 --- /dev/null +++ b/api/src/bankerx/api/Codecs.scala @@ -0,0 +1,12 @@ +package bankerx.api +import bankerx.API.* +import com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec +import com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker + +trait Codecs: + given categoryJsonValueCodec: JsonValueCodec[Category] = + JsonCodecMaker.makeWithoutDiscriminator + given purchaseJsonValueCodec: JsonValueCodec[Purchase] = JsonCodecMaker.make + given termsJsonValueCodec: JsonValueCodec[Terms] = JsonCodecMaker.make + +object Codecs extends Codecs diff --git a/api/src/bankerx/api/PublicEndpoints.scala b/api/src/bankerx/api/PublicEndpoints.scala new file mode 100644 index 00000000..7c861efb --- /dev/null +++ b/api/src/bankerx/api/PublicEndpoints.scala @@ -0,0 +1,20 @@ +package bankerx.api +import sttp.tapir.* +import sttp.tapir.generic.auto.* +import sttp.tapir.json.jsoniter.* + +import sttp.shared.Identity +import sttp.tapir.server.ServerEndpoint +import com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec +import com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker +import bankerx.API.* +import bankerx.api.Codecs.{given, *} + +object PublicEndpoints: + val getTermsEndpoint + : PublicEndpoint[(BankName, Purchase), String, Terms, Any] = + endpoint.post + .in("api" / "bank" / path[BankName]("bankName") / "terms") + .in(jsonBody[Purchase]) + .out(jsonBody[Terms]) + .errorOut(stringBody) diff --git a/api/src/bankerx/api/tools/FDC3Backend.scala b/api/src/bankerx/api/tools/FDC3Backend.scala new file mode 100644 index 00000000..77322a06 --- /dev/null +++ b/api/src/bankerx/api/tools/FDC3Backend.scala @@ -0,0 +1,66 @@ +import morphir.ir._ +import morphir.ir.module._ +import morphir.ir.module.Codec._ + +import io.circe.Json +import io.circe.parser.parse +import morphir.ir.Distribution.Distribution +import morphir.ir.Documented.Documented +import morphir.ir.formatversion.Codec.decodeVersionedDistribution +import morphir.ir.{AccessControlled, Name,Path, Type, Value} + +import scala.io.Source +import scala.util.{Try, Using} + +object FDC3Backend extends App { + loadMorphirIR("api/morphir-ir.json") match { + case scala.util.Success(ir) => processIR(ir) + case scala.util.Failure(exception) => println(s"Failed to load IR: $exception") + } + + def loadMorphirIR(irPath : String): Try[Distribution] = { + Using(Source.fromFile(irPath)) { source => + val source = Source.fromFile(irPath) + val json = source.mkString + val parsedIR = parse(json).getOrElse(Json.Null).hcursor + val distribution = decodeVersionedDistribution(parsedIR) + + distribution match { + case Left(err) => throw new Exception(s"Error decoding distribution: $err") + case Right(value) => value.distribution + } + } + } + + def processIR(ir: Distribution): Unit = { + ir match { + case Distribution.Library(_, _, packageDef) => + packageDef.modules.toList.foreach { (moduleName, module) => + module.value match { + case Module.Definition(types, _, _) => { + types.toList.foreach { (typeName, typeDef) => { + typeDef.value.value match { + case Type.Definition.TypeAliasDefinition(_, typeOf) => { + typeOf match { + case Type.Record(_, fields) => { + println(s"Record: ${typeName}") + fields.foreach { field => + println(s"\tField: ${field.name}, Type: ${field.tpe}") + } + } + case t => + println(s"Type alias: ${t.getClass}") + } + } + case Type.Definition.CustomTypeDefinition(_, typeOf) => { + println(s"Custom Type: ${typeOf.getClass}") + } + } + } + } + } + } + } + } + } +} diff --git a/API/src/main/scala/Main.scala b/api/src/bankerx/api/tools/Main.scala similarity index 92% rename from API/src/main/scala/Main.scala rename to api/src/bankerx/api/tools/Main.scala index a944cf4d..efae2407 100644 --- a/API/src/main/scala/Main.scala +++ b/api/src/bankerx/api/tools/Main.scala @@ -1,4 +1,4 @@ -import bankerx.FirstBank +import bankerx.banks.FirstBank import bankerx.API.Purchase import java.time.LocalDate import java.time.LocalTime diff --git a/api/test/src/bankerx/api/CodecsSpec.scala b/api/test/src/bankerx/api/CodecsSpec.scala new file mode 100644 index 00000000..13852788 --- /dev/null +++ b/api/test/src/bankerx/api/CodecsSpec.scala @@ -0,0 +1,42 @@ +package bankerx.api +import org.scalatest.* +import wordspec.* +import matchers.* +import bankerx.API.* +import com.github.plokhotnyuk.jsoniter_scala.macros.* +import com.github.plokhotnyuk.jsoniter_scala.core.* + +class CodecsSpec extends AnyWordSpec with should.Matchers with Codecs: + "Codecs" when { + "given a Purchase" should { + "be able to encode and decode it" in { + val purchase = Purchase( + 100, + "Vender1", + java.time.LocalDate.now(), + java.time.LocalTime.now(), + "user 1", + Fuel, + "pointOfSale 1" + ) + val json = writeToString(purchase) + println(json) + val decodedPurchase = readFromString[Purchase](json) + decodedPurchase.shouldEqual(purchase) + } + } + + "given a Terms" should { + "be able to encode and decode it" in { + val terms = Terms( + "Provider1", + 100, + 0.1, + 12 + ) + val json = writeToString(terms) + val decodedTerms = readFromString[Terms](json) + decodedTerms.shouldEqual(terms) + } + } + } diff --git a/app/bank-app1.html b/app/bank-app1.html index b33cd58c..571608ca 100644 --- a/app/bank-app1.html +++ b/app/bank-app1.html @@ -32,6 +32,7 @@ logo: './images/etrade.png' } } + }; }); fdc3.addIntentListener('MakePurchase', async (ctx) => { diff --git a/app/main.js b/app/main.js index c629f51e..437c4ee7 100644 --- a/app/main.js +++ b/app/main.js @@ -33,6 +33,7 @@ const launchBankApp = (index) => { }; const showSuccessModal = (message, purchaseResult) => { + const modal = document.getElementById('successModal'); const modalCTA = document.getElementById('successCTA'); modalCTA.addEventListener('click', () => { hideModal('successModal');}); diff --git a/app/styles.css b/app/styles.css index b655db41..2112c54d 100644 --- a/app/styles.css +++ b/app/styles.css @@ -186,6 +186,10 @@ body { border-bottom:dashed 1px #222; } + .card .header .logo img { + max-height: 60px; + } + .card .header .text { font-size: 24px; } @@ -218,6 +222,18 @@ body { align-items: center; } + #succesModal .row { + display: flex; + flex-flow: row; + justify-content: center; + } + + + #successModal .logo img { + height: 60px; + } + + #successModal .text { font-size: 24px; font-weight: 700; diff --git a/build.mill b/build.mill new file mode 100644 index 00000000..f2cad23e --- /dev/null +++ b/build.mill @@ -0,0 +1,10 @@ +package build +import mill._, scalalib._ +import $file.versions +import $file.util + +object `package` extends RootModule with Module { + def cdkSynth = T { + + } +} diff --git a/cdk/.gitignore b/cdk/.gitignore new file mode 100644 index 00000000..29eb7147 --- /dev/null +++ b/cdk/.gitignore @@ -0,0 +1,9 @@ +*.js +!jest.config.js +*.d.ts +node_modules +package-lock.json + +# CDK asset staging directory +.cdk.staging +cdk.out \ No newline at end of file diff --git a/cdk/bin/bankerx-cdk-stack.ts b/cdk/bin/bankerx-cdk-stack.ts new file mode 100755 index 00000000..08325679 --- /dev/null +++ b/cdk/bin/bankerx-cdk-stack.ts @@ -0,0 +1,9 @@ +#!/usr/bin/env node +import * as cdk from "aws-cdk-lib"; +import { BankerXCdkStack } from "../lib/bankerx-cdk-stack"; +import * as process from "node:process"; +import * as path from "node:path"; +import { existsSync } from "node:fs"; + +const app = new cdk.App(); +new BankerXCdkStack(app, "BankerXCDK"); diff --git a/cdk/cdk.json b/cdk/cdk.json new file mode 100644 index 00000000..02236b93 --- /dev/null +++ b/cdk/cdk.json @@ -0,0 +1,40 @@ +{ + "app": "npx ts-node --prefer-ts-exts ./bin/bankerx-cdk-stack.ts", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "**/*.d.ts", + "**/*.js", + "tsconfig.json", + "package*.json", + "yarn.lock", + "node_modules", + "test" + ] + }, + "context": { + "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, + "@aws-cdk/core:stackRelativeExports": true, + "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, + "@aws-cdk/aws-lambda:recognizeVersionProps": true, + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ] + } +} \ No newline at end of file diff --git a/cdk/jest.config.js b/cdk/jest.config.js new file mode 100644 index 00000000..560c2600 --- /dev/null +++ b/cdk/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + testEnvironment: 'node', + roots: ['/test'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest' + } +}; \ No newline at end of file diff --git a/cdk/lib/bankerx-cdk-stack.ts b/cdk/lib/bankerx-cdk-stack.ts new file mode 100644 index 00000000..e2e6c9ea --- /dev/null +++ b/cdk/lib/bankerx-cdk-stack.ts @@ -0,0 +1,57 @@ +import * as cdk from "aws-cdk-lib"; +import * as lambda from "aws-cdk-lib/aws-lambda"; +import * as apigw from "aws-cdk-lib/aws-apigateway"; +import * as process from "node:process"; +import * as path from "node:path"; +import { existsSync } from "node:fs"; + +export class BankerXCdkStack extends cdk.Stack { + constructor( + scope: cdk.App, + id: string, + props?: cdk.StackProps + ) { + super(scope, id, props); + + const codeAssetPath = process.env["BANKERX_LAMBDA_CODE_ASSET"]; + if (!codeAssetPath) { + const errorMessage = + "BANKERX_LAMBDA_CODE_ASSET environment variable is not set, this is required to deploy the BankerX Lambda. Set it to the path of the BankerX Lambda code asset."; + console.error(errorMessage); + throw new Error(errorMessage); + } + + const normalizedCodeAssetPath = path.normalize(codeAssetPath); + + if (!existsSync(normalizedCodeAssetPath)) { + const errorMessage = `The BANKERX_LAMBDA_CODE_ASSET environment variable points to a path that does not exist: ${normalizedCodeAssetPath}`; + console.error(errorMessage); + throw new Error(errorMessage); + } + + console.info(`codeAssetPath: ${codeAssetPath}`); + + const lambdaJar = new lambda.Function(this, "BankerX", { + runtime: lambda.Runtime.JAVA_11, + code: lambda.Code.fromAsset(codeAssetPath), + handler: "bankerx.serverless.BankerXLambdaHandler::handleRequest", + timeout: cdk.Duration.seconds(20), + memorySize: 2048, + }); + + const api = new apigw.LambdaRestApi(this, "BankerXApi", { + handler: lambdaJar, + proxy: false, + }); + + const rootApi = api.root.addResource("api"); + + // Create a resource for performing a post to bank/{bankName}/terms + const rootApiBankerTerms = rootApi.addResource("bank"); + const rootApiBankerTermsBankName = + rootApiBankerTerms.addResource("{bankName}"); + const rootApiBankerTermsBankNameTerms = + rootApiBankerTermsBankName.addResource("terms"); + rootApiBankerTermsBankNameTerms.addMethod("POST"); + } +} diff --git a/cdk/package.json b/cdk/package.json new file mode 100644 index 00000000..c143e544 --- /dev/null +++ b/cdk/package.json @@ -0,0 +1,28 @@ +{ + "name": "tapir-cdk", + "version": "0.1.0", + "bin": { + "bankerx-cdk": "bin/bankerx-cdk-stack.ts" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "jest", + "cdk": "cdk" + }, + "devDependencies": { + "@types/jest": "29.5.13", + "@types/node": "^20.16.10", + "@types/prettier": "2.6.0", + "@aws-cdk/assert": "^2.68.0", + "aws-cdk": "2.160.0", + "jest": "29.7.0", + "ts-jest": "29.2.5", + "ts-node": "^10.9.1", + "typescript": "5.6.2" + }, + "dependencies": { + "aws-cdk-lib": "2.160.0", + "constructs": "^10.0.0" + } +} \ No newline at end of file diff --git a/cdk/package.mill b/cdk/package.mill new file mode 100644 index 00000000..a8f52a38 --- /dev/null +++ b/cdk/package.mill @@ -0,0 +1,81 @@ +package build.cdk +import mill._, scalalib._ +import $file.versions._ +import $file.util._ + +object `package` extends RootModule with NodeModule { + def allSourceFiles = T {} + def mirror = T { PathRef(T.dest) } + + def sourceRoot = T { PathRef(millSourcePath)} + + def cdkBootstrap = T { + val workingDir = prepareCdkForSynth().path + val _ = cdkSynth() + val assetJar = build.serverless.assembly().path + val env = T.env ++ Map("BANKERX_LAMBDA_CODE_ASSET"-> assetJar.toString) + os.proc("npx", "cdk", "bootstrap").call(cwd = workingDir, env = env) + PathRef(workingDir) + } + + def cdkDeploy = T { + val workingDir = prepareCdkForSynth().path + val _ = cdkSynth() + val assetJar = build.serverless.assembly().path + val env = T.env ++ Map("BANKERX_LAMBDA_CODE_ASSET"-> assetJar.toString) + os.proc("npx", "cdk", "deploy", "--require-approval=never").call(cwd = workingDir, env = env) + PathRef(workingDir) + } + + def cdkSynth = T { + val workingDir = prepareCdkForSynth().path + val assetJar = build.serverless.assembly().path + val env = T.env ++ Map("BANKERX_LAMBDA_CODE_ASSET"-> assetJar.toString) + os.proc("npx", "cdk", "synth").call(cwd = workingDir, env = env) + PathRef(workingDir / "cdk.out") + } + + def cdkSources = T { + val workingDir = mirror().path + val sourceFolder = sourceRoot().path + val destinationFolder = workingDir / "cdk" + + os.copy(sourceFolder, destinationFolder) + // Want to ensure node modules are setup separately + os.remove.all(destinationFolder / "node_modules") + PathRef(destinationFolder) + } + + def prepareCdkForSynth = T { + val _ = npmInstallMirror() + val mirroredSrcs = cdkSources().path + PathRef(mirroredSrcs) + } + + def synthesizedTemplateRoot = T { + PathRef(cdkSynth().path) + } + + def templateFilename = T {"BankerXCdkStack.template.json"} + + def npmInstallMirror = T { + npmInstallAction(cdkSources().path) + } + + def runSamLocal(customImage:Option[String] = None, skipPullImage:Boolean = false) = T.command { + val templateRoot = synthesizedTemplateRoot().path + val templatePath = templateRoot / templateFilename() + var cmd:Seq[os.Shellable] = Seq("sam", "local", "start-api", "-t", templatePath.toString) + if (skipPullImage) { + cmd = cmd ++ Seq("--skip-pull-image") + } + customImage.foreach { image => + cmd = cmd ++ Seq("--invoke-image", image) + } + os.proc(cmd:_*).call(cwd = templateRoot) + val finalCmd = cmd.map(_.toString) + Map( + ("command", finalCmd) + ) + } +} \ No newline at end of file diff --git a/cdk/readme.md b/cdk/readme.md new file mode 100644 index 00000000..3d583131 --- /dev/null +++ b/cdk/readme.md @@ -0,0 +1,20 @@ +# AWS CDK Stack + +## Prerequisites + +- Install [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) +- Install [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +- Install [AWS CDK Toolkit](https://docs.aws.amazon.com/cdk/v2/guide/cli.html) + +## Run service locally + +``` +npm install +cdk synth +sam local start-api -t cdk.out/TapirCdkStack.template.json +``` + +## Deploy to production + +- Configure your account ```aws configure``` +- ```cdk deploy``` \ No newline at end of file diff --git a/cdk/tsconfig.json b/cdk/tsconfig.json new file mode 100644 index 00000000..a74ab403 --- /dev/null +++ b/cdk/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "ES2018", + "module": "commonjs", + "lib": [ + "es2018" + ], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "./node_modules/@types" + ] + }, + "exclude": [ + "node_modules", + "cdk.out" + ] +} \ No newline at end of file diff --git a/mill b/mill new file mode 100755 index 00000000..1c162d2c --- /dev/null +++ b/mill @@ -0,0 +1,241 @@ +#!/usr/bin/env sh + +# This is a wrapper script, that automatically download mill from GitHub release pages +# You can give the required mill version with --mill-version parameter +# If no version is given, it falls back to the value of DEFAULT_MILL_VERSION +# +# Project page: https://github.com/lefou/millw +# Script Version: 0.4.12 +# +# If you want to improve this script, please also contribute your changes back! +# +# Licensed under the Apache License, Version 2.0 + +set -e + +if [ -z "${DEFAULT_MILL_VERSION}" ] ; then + DEFAULT_MILL_VERSION="0.11.4" +fi + + +if [ -z "${GITHUB_RELEASE_CDN}" ] ; then + GITHUB_RELEASE_CDN="" +fi + + +MILL_REPO_URL="https://github.com/com-lihaoyi/mill" + +if [ -z "${CURL_CMD}" ] ; then + CURL_CMD=curl +fi + +# Explicit commandline argument takes precedence over all other methods +if [ "$1" = "--mill-version" ] ; then + shift + if [ "x$1" != "x" ] ; then + MILL_VERSION="$1" + shift + else + echo "You specified --mill-version without a version." 1>&2 + echo "Please provide a version that matches one provided on" 1>&2 + echo "${MILL_REPO_URL}/releases" 1>&2 + false + fi +fi + +# Please note, that if a MILL_VERSION is already set in the environment, +# We reuse it's value and skip searching for a value. + +# If not already set, read .mill-version file +if [ -z "${MILL_VERSION}" ] ; then + if [ -f ".mill-version" ] ; then + MILL_VERSION="$(head -n 1 .mill-version 2> /dev/null)" + elif [ -f ".config/mill-version" ] ; then + MILL_VERSION="$(head -n 1 .config/mill-version 2> /dev/null)" + fi +fi + +MILL_USER_CACHE_DIR="${XDG_CACHE_HOME:-${HOME}/.cache}/mill" + +if [ -z "${MILL_DOWNLOAD_PATH}" ] ; then + MILL_DOWNLOAD_PATH="${MILL_USER_CACHE_DIR}/download" +fi + +# If not already set, try to fetch newest from Github +if [ -z "${MILL_VERSION}" ] ; then + # TODO: try to load latest version from release page + echo "No mill version specified." 1>&2 + echo "You should provide a version via '.mill-version' file or --mill-version option." 1>&2 + + mkdir -p "${MILL_DOWNLOAD_PATH}" + LANG=C touch -d '1 hour ago' "${MILL_DOWNLOAD_PATH}/.expire_latest" 2>/dev/null || ( + # we might be on OSX or BSD which don't have -d option for touch + # but probably a -A [-][[hh]mm]SS + touch "${MILL_DOWNLOAD_PATH}/.expire_latest"; touch -A -010000 "${MILL_DOWNLOAD_PATH}/.expire_latest" + ) || ( + # in case we still failed, we retry the first touch command with the intention + # to show the (previously suppressed) error message + LANG=C touch -d '1 hour ago' "${MILL_DOWNLOAD_PATH}/.expire_latest" + ) + + # POSIX shell variant of bash's -nt operator, see https://unix.stackexchange.com/a/449744/6993 + # if [ "${MILL_DOWNLOAD_PATH}/.latest" -nt "${MILL_DOWNLOAD_PATH}/.expire_latest" ] ; then + if [ -n "$(find -L "${MILL_DOWNLOAD_PATH}/.latest" -prune -newer "${MILL_DOWNLOAD_PATH}/.expire_latest")" ]; then + # we know a current latest version + MILL_VERSION=$(head -n 1 "${MILL_DOWNLOAD_PATH}"/.latest 2> /dev/null) + fi + + if [ -z "${MILL_VERSION}" ] ; then + # we don't know a current latest version + echo "Retrieving latest mill version ..." 1>&2 + LANG=C ${CURL_CMD} -s -i -f -I ${MILL_REPO_URL}/releases/latest 2> /dev/null | grep --ignore-case Location: | sed s'/^.*tag\///' | tr -d '\r\n' > "${MILL_DOWNLOAD_PATH}/.latest" + MILL_VERSION=$(head -n 1 "${MILL_DOWNLOAD_PATH}"/.latest 2> /dev/null) + fi + + if [ -z "${MILL_VERSION}" ] ; then + # Last resort + MILL_VERSION="${DEFAULT_MILL_VERSION}" + echo "Falling back to hardcoded mill version ${MILL_VERSION}" 1>&2 + else + echo "Using mill version ${MILL_VERSION}" 1>&2 + fi +fi + +MILL="${MILL_DOWNLOAD_PATH}/${MILL_VERSION}" + +try_to_use_system_mill() { + if [ "$(uname)" != "Linux" ]; then + return 0 + fi + + MILL_IN_PATH="$(command -v mill || true)" + + if [ -z "${MILL_IN_PATH}" ]; then + return 0 + fi + + SYSTEM_MILL_FIRST_TWO_BYTES=$(head --bytes=2 "${MILL_IN_PATH}") + if [ "${SYSTEM_MILL_FIRST_TWO_BYTES}" = "#!" ]; then + # MILL_IN_PATH is (very likely) a shell script and not the mill + # executable, ignore it. + return 0 + fi + + SYSTEM_MILL_PATH=$(readlink -e "${MILL_IN_PATH}") + SYSTEM_MILL_SIZE=$(stat --format=%s "${SYSTEM_MILL_PATH}") + SYSTEM_MILL_MTIME=$(stat --format=%y "${SYSTEM_MILL_PATH}") + + if [ ! -d "${MILL_USER_CACHE_DIR}" ]; then + mkdir -p "${MILL_USER_CACHE_DIR}" + fi + + SYSTEM_MILL_INFO_FILE="${MILL_USER_CACHE_DIR}/system-mill-info" + if [ -f "${SYSTEM_MILL_INFO_FILE}" ]; then + parseSystemMillInfo() { + LINE_NUMBER="${1}" + # Select the line number of the SYSTEM_MILL_INFO_FILE, cut the + # variable definition in that line in two halves and return + # the value, and finally remove the quotes. + sed -n "${LINE_NUMBER}p" "${SYSTEM_MILL_INFO_FILE}" |\ + cut -d= -f2 |\ + sed 's/"\(.*\)"/\1/' + } + + CACHED_SYSTEM_MILL_PATH=$(parseSystemMillInfo 1) + CACHED_SYSTEM_MILL_VERSION=$(parseSystemMillInfo 2) + CACHED_SYSTEM_MILL_SIZE=$(parseSystemMillInfo 3) + CACHED_SYSTEM_MILL_MTIME=$(parseSystemMillInfo 4) + + if [ "${SYSTEM_MILL_PATH}" = "${CACHED_SYSTEM_MILL_PATH}" ] \ + && [ "${SYSTEM_MILL_SIZE}" = "${CACHED_SYSTEM_MILL_SIZE}" ] \ + && [ "${SYSTEM_MILL_MTIME}" = "${CACHED_SYSTEM_MILL_MTIME}" ]; then + if [ "${CACHED_SYSTEM_MILL_VERSION}" = "${MILL_VERSION}" ]; then + MILL="${SYSTEM_MILL_PATH}" + return 0 + else + return 0 + fi + fi + fi + + SYSTEM_MILL_VERSION=$(${SYSTEM_MILL_PATH} --version | head -n1 | sed -n 's/^Mill.*version \(.*\)/\1/p') + + cat < "${SYSTEM_MILL_INFO_FILE}" +CACHED_SYSTEM_MILL_PATH="${SYSTEM_MILL_PATH}" +CACHED_SYSTEM_MILL_VERSION="${SYSTEM_MILL_VERSION}" +CACHED_SYSTEM_MILL_SIZE="${SYSTEM_MILL_SIZE}" +CACHED_SYSTEM_MILL_MTIME="${SYSTEM_MILL_MTIME}" +EOF + + if [ "${SYSTEM_MILL_VERSION}" = "${MILL_VERSION}" ]; then + MILL="${SYSTEM_MILL_PATH}" + fi +} +try_to_use_system_mill + +# If not already downloaded, download it +if [ ! -s "${MILL}" ] ; then + + # support old non-XDG download dir + MILL_OLD_DOWNLOAD_PATH="${HOME}/.mill/download" + OLD_MILL="${MILL_OLD_DOWNLOAD_PATH}/${MILL_VERSION}" + if [ -x "${OLD_MILL}" ] ; then + MILL="${OLD_MILL}" + else + case $MILL_VERSION in + 0.0.* | 0.1.* | 0.2.* | 0.3.* | 0.4.* ) + DOWNLOAD_SUFFIX="" + DOWNLOAD_FROM_MAVEN=0 + ;; + 0.5.* | 0.6.* | 0.7.* | 0.8.* | 0.9.* | 0.10.* | 0.11.0-M* ) + DOWNLOAD_SUFFIX="-assembly" + DOWNLOAD_FROM_MAVEN=0 + ;; + *) + DOWNLOAD_SUFFIX="-assembly" + DOWNLOAD_FROM_MAVEN=1 + ;; + esac + + DOWNLOAD_FILE=$(mktemp mill.XXXXXX) + + if [ "$DOWNLOAD_FROM_MAVEN" = "1" ] ; then + DOWNLOAD_URL="https://repo1.maven.org/maven2/com/lihaoyi/mill-dist/${MILL_VERSION}/mill-dist-${MILL_VERSION}.jar" + else + MILL_VERSION_TAG=$(echo "$MILL_VERSION" | sed -E 's/([^-]+)(-M[0-9]+)?(-.*)?/\1\2/') + DOWNLOAD_URL="${GITHUB_RELEASE_CDN}${MILL_REPO_URL}/releases/download/${MILL_VERSION_TAG}/${MILL_VERSION}${DOWNLOAD_SUFFIX}" + unset MILL_VERSION_TAG + fi + + # TODO: handle command not found + echo "Downloading mill ${MILL_VERSION} from ${DOWNLOAD_URL} ..." 1>&2 + ${CURL_CMD} -f -L -o "${DOWNLOAD_FILE}" "${DOWNLOAD_URL}" + chmod +x "${DOWNLOAD_FILE}" + mkdir -p "${MILL_DOWNLOAD_PATH}" + mv "${DOWNLOAD_FILE}" "${MILL}" + + unset DOWNLOAD_FILE + unset DOWNLOAD_SUFFIX + fi +fi + +if [ -z "$MILL_MAIN_CLI" ] ; then + MILL_MAIN_CLI="${0}" +fi + +MILL_FIRST_ARG="" +if [ "$1" = "--bsp" ] || [ "$1" = "-i" ] || [ "$1" = "--interactive" ] || [ "$1" = "--no-server" ] || [ "$1" = "--repl" ] || [ "$1" = "--help" ] ; then + # Need to preserve the first position of those listed options + MILL_FIRST_ARG=$1 + shift +fi + +unset MILL_DOWNLOAD_PATH +unset MILL_OLD_DOWNLOAD_PATH +unset OLD_MILL +unset MILL_VERSION +unset MILL_REPO_URL + +# We don't quote MILL_FIRST_ARG on purpose, so we can expand the empty value without quotes +# shellcheck disable=SC2086 +exec "${MILL}" $MILL_FIRST_ARG -D "mill.main.cli=${MILL_MAIN_CLI}" "$@" diff --git a/mill.bat b/mill.bat new file mode 100644 index 00000000..c81a35d9 --- /dev/null +++ b/mill.bat @@ -0,0 +1,220 @@ +@echo off + +rem This is a wrapper script, that automatically download mill from GitHub release pages +rem You can give the required mill version with --mill-version parameter +rem If no version is given, it falls back to the value of DEFAULT_MILL_VERSION +rem +rem Project page: https://github.com/lefou/millw +rem Script Version: 0.4.12 +rem +rem If you want to improve this script, please also contribute your changes back! +rem +rem Licensed under the Apache License, Version 2.0 + +rem setlocal seems to be unavailable on Windows 95/98/ME +rem but I don't think we need to support them in 2019 +setlocal enabledelayedexpansion + +if [!DEFAULT_MILL_VERSION!]==[] ( + set "DEFAULT_MILL_VERSION=0.11.4" +) + +if [!GITHUB_RELEASE_CDN!]==[] ( + set "GITHUB_RELEASE_CDN=" +) + +if [!MILL_MAIN_CLI!]==[] ( + set "MILL_MAIN_CLI=%~f0" +) + +set "MILL_REPO_URL=https://github.com/com-lihaoyi/mill" + +rem %~1% removes surrounding quotes +if [%~1%]==[--mill-version] ( + if not [%~2%]==[] ( + set MILL_VERSION=%~2% + rem shift command doesn't work within parentheses + set "STRIP_VERSION_PARAMS=true" + ) else ( + echo You specified --mill-version without a version. 1>&2 + echo Please provide a version that matches one provided on 1>&2 + echo %MILL_REPO_URL%/releases 1>&2 + exit /b 1 + ) +) + +if not defined STRIP_VERSION_PARAMS GOTO AfterStripVersionParams +rem strip the: --mill-version {version} +shift +shift +:AfterStripVersionParams + +if [!MILL_VERSION!]==[] ( + if exist .mill-version ( + set /p MILL_VERSION=<.mill-version + ) else ( + if exist .config\mill-version ( + set /p MILL_VERSION=<.config\mill-version + ) + ) +) + +if [!MILL_VERSION!]==[] ( + set MILL_VERSION=%DEFAULT_MILL_VERSION% +) + +if [!MILL_DOWNLOAD_PATH!]==[] ( + set MILL_DOWNLOAD_PATH=%USERPROFILE%\.mill\download +) + +rem without bat file extension, cmd doesn't seem to be able to run it +set MILL=%MILL_DOWNLOAD_PATH%\!MILL_VERSION!.bat + +if not exist "%MILL%" ( + set VERSION_PREFIX=%MILL_VERSION:~0,4% + rem Since 0.5.0 + set DOWNLOAD_SUFFIX=-assembly + rem Since 0.11.0 + set DOWNLOAD_FROM_MAVEN=1 + if [!VERSION_PREFIX!]==[0.0.] ( + set DOWNLOAD_SUFFIX= + set DOWNLOAD_FROM_MAVEN=0 + ) + if [!VERSION_PREFIX!]==[0.1.] ( + set DOWNLOAD_SUFFIX= + set DOWNLOAD_FROM_MAVEN=0 + ) + if [!VERSION_PREFIX!]==[0.2.] ( + set DOWNLOAD_SUFFIX= + set DOWNLOAD_FROM_MAVEN=0 + ) + if [!VERSION_PREFIX!]==[0.3.] ( + set DOWNLOAD_SUFFIX= + set DOWNLOAD_FROM_MAVEN=0 + ) + if [!VERSION_PREFIX!]==[0.4.] ( + set DOWNLOAD_SUFFIX= + set DOWNLOAD_FROM_MAVEN=0 + ) + if [!VERSION_PREFIX!]==[0.5.] ( + set DOWNLOAD_FROM_MAVEN=0 + ) + if [!VERSION_PREFIX!]==[0.6.] ( + set DOWNLOAD_FROM_MAVEN=0 + ) + if [!VERSION_PREFIX!]==[0.7.] ( + set DOWNLOAD_FROM_MAVEN=0 + ) + if [!VERSION_PREFIX!]==[0.8.] ( + set DOWNLOAD_FROM_MAVEN=0 + ) + if [!VERSION_PREFIX!]==[0.9.] ( + set DOWNLOAD_FROM_MAVEN=0 + ) + set VERSION_PREFIX=%MILL_VERSION:~0,5% + if [!VERSION_PREFIX!]==[0.10.] ( + set DOWNLOAD_FROM_MAVEN=0 + ) + set VERSION_PREFIX=%MILL_VERSION:~0,8% + if [!VERSION_PREFIX!]==[0.11.0-M] ( + set DOWNLOAD_FROM_MAVEN=0 + ) + set VERSION_PREFIX= + + for /F "delims=- tokens=1" %%A in ("!MILL_VERSION!") do set MILL_VERSION_BASE=%%A + for /F "delims=- tokens=2" %%A in ("!MILL_VERSION!") do set MILL_VERSION_MILESTONE=%%A + set VERSION_MILESTONE_START=!MILL_VERSION_MILESTONE:~0,1! + if [!VERSION_MILESTONE_START!]==[M] ( + set MILL_VERSION_TAG="!MILL_VERSION_BASE!-!MILL_VERSION_MILESTONE!" + ) else ( + set MILL_VERSION_TAG=!MILL_VERSION_BASE! + ) + + rem there seems to be no way to generate a unique temporary file path (on native Windows) + set DOWNLOAD_FILE=%MILL%.tmp + + if [!DOWNLOAD_FROM_MAVEN!]==[1] ( + set DOWNLOAD_URL=https://repo1.maven.org/maven2/com/lihaoyi/mill-dist/!MILL_VERSION!/mill-dist-!MILL_VERSION!.jar + ) else ( + set DOWNLOAD_URL=!GITHUB_RELEASE_CDN!%MILL_REPO_URL%/releases/download/!MILL_VERSION_TAG!/!MILL_VERSION!!DOWNLOAD_SUFFIX! + ) + + echo Downloading mill %MILL_VERSION% from !DOWNLOAD_URL! ... 1>&2 + + if not exist "%MILL_DOWNLOAD_PATH%" mkdir "%MILL_DOWNLOAD_PATH%" + rem curl is bundled with recent Windows 10 + rem but I don't think we can expect all the users to have it in 2019 + where /Q curl + if %ERRORLEVEL% EQU 0 ( + curl -f -L "!DOWNLOAD_URL!" -o "!DOWNLOAD_FILE!" + ) else ( + rem bitsadmin seems to be available on Windows 7 + rem without /dynamic, github returns 403 + rem bitsadmin is sometimes needlessly slow but it looks better with /priority foreground + bitsadmin /transfer millDownloadJob /dynamic /priority foreground "!DOWNLOAD_URL!" "!DOWNLOAD_FILE!" + ) + if not exist "!DOWNLOAD_FILE!" ( + echo Could not download mill %MILL_VERSION% 1>&2 + exit /b 1 + ) + + move /y "!DOWNLOAD_FILE!" "%MILL%" + + set DOWNLOAD_FILE= + set DOWNLOAD_SUFFIX= +) + +set MILL_DOWNLOAD_PATH= +set MILL_VERSION= +set MILL_REPO_URL= + +rem Need to preserve the first position of those listed options +set MILL_FIRST_ARG= +if [%~1%]==[--bsp] ( + set MILL_FIRST_ARG=%1% +) else ( + if [%~1%]==[-i] ( + set MILL_FIRST_ARG=%1% + ) else ( + if [%~1%]==[--interactive] ( + set MILL_FIRST_ARG=%1% + ) else ( + if [%~1%]==[--no-server] ( + set MILL_FIRST_ARG=%1% + ) else ( + if [%~1%]==[--repl] ( + set MILL_FIRST_ARG=%1% + ) else ( + if [%~1%]==[--help] ( + set MILL_FIRST_ARG=%1% + ) + ) + ) + ) + ) +) + +set "MILL_PARAMS=%*%" + +if not [!MILL_FIRST_ARG!]==[] ( + if defined STRIP_VERSION_PARAMS ( + for /f "tokens=1-3*" %%a in ("%*") do ( + set "MILL_PARAMS=%%d" + ) + ) else ( + for /f "tokens=1*" %%a in ("%*") do ( + set "MILL_PARAMS=%%b" + ) + ) +) else ( + if defined STRIP_VERSION_PARAMS ( + for /f "tokens=1-2*" %%a in ("%*") do ( + rem strip %%a - It's the "--mill-version" option. + rem strip %%b - it's the version number that comes after the option. + rem keep %%c - It's the remaining options. + set "MILL_PARAMS=%%c" + ) + ) +) + +"%MILL%" %MILL_FIRST_ARG% -D "mill.main.cli=%MILL_MAIN_CLI%" %MILL_PARAMS% diff --git a/mill.ps1 b/mill.ps1 new file mode 100644 index 00000000..668e35c3 --- /dev/null +++ b/mill.ps1 @@ -0,0 +1,137 @@ +# This is a wrapper script, that automatically download mill from GitHub release pages +# You can give the required mill version with --mill-version parameter +# If no version is given, it falls back to the value of DEFAULT_MILL_VERSION +# +# Project page: https://github.com/lefou/millw +# Script Version: 0.4.12 +# +# If you want to improve this script, please also contribute your changes back! +# +# Licensed under the Apache License, Version 2.0 + +[CmdletBinding(PositionalBinding = $false)] + +param( + [Parameter(ValueFromRemainingArguments = $true, Position = 0)] + [string[]] $remainingArgs +) + +$DEFAULT_MILL_VERSION = $Env:DEFAULT_MILL_VERSION ?? '0.11.6' + +$GITHUB_RELEASE_CDN = $Env:GITHUB_RELEASE_CDN ?? '' + +$MILL_REPO_URL = 'https://github.com/com-lihaoyi/mill' + +$MILL_VERSION = $null + +if ($null -ne $remainingArgs) { + if ($remainingArgs[0] -eq '--mill-version') { + $remainingArgs = Select-Object -InputObject $remainingArgs -Skip 1 + if ($null -ne $remainingArgs) { + $MILL_VERSION = $remainingArgs[0] + $remainingArgs = Select-Object -InputObject $remainingArgs -Skip 1 + } + else { + Write-Error -Message "Please provide a version that matches one provided on $MILL_REPO_URL/releases" + throw [System.ArgumentNullException] '--mill-version' + } + } +} + +if ($null -eq $MILL_VERSION) { + if (Test-Path -Path '.mill-version' -PathType Leaf) { + $MILL_VERSION = Get-Content -Path '.mill-version' -TotalCount 1 + } + elseif (Test-Path -Path '.config/mill-version' -PathType Leaf) { + $MILL_VERSION = Get-Content -Path '.config/mill-version' -TotalCount 1 + } +} + +$MILL_USER_CACHE_DIR = Join-Path -Path $Env:LOCALAPPDATA -ChildPath 'mill' + +$MILL_DOWNLOAD_PATH = $Env:MILL_DOWNLOAD_PATH ?? @(Join-Path -Path ${MILL_USER_CACHE_DIR} -ChildPath 'download') + +if (-not (Test-Path -Path $MILL_DOWNLOAD_PATH)) { + New-Item -Path $MILL_DOWNLOAD_PATH -ItemType Directory | Out-Null +} + +if ($null -eq $MILL_VERSION) { + Write-Warning -Message 'No mill version specified.' + Write-Warning -Message "You should provide a version via '.mill-version' file or --mill-version option." + + if (-not (Test-Path -Path "$MILL_DOWNLOAD_PATH" -PathType Container)) { + New-Item "$MILL_DOWNLOAD_PATH" -ItemType Directory | Out-Null + } + + $MILL_LATEST_PATH = Join-Path -Path $MILL_DOWNLOAD_PATH -ChildPath '.latest' + + if (Test-Path -Path $MILL_LATEST_PATH -PathType Leaf) { + if ($(Get-Item -Path $MILL_LATEST_PATH).LastWriteTime -lt $(Get-Date).AddHours(-1)) { + $MILL_VERSION = Get-Content -Path $MILL_LATEST_PATH -TotalCount 1 + } + } + + if ($null -eq $MILL_VERSION) { + Write-Output 'Retrieving latest mill version ...' + + # https://github.com/PowerShell/PowerShell/issues/20964 + $targetUrl = try { + Invoke-WebRequest -Uri "$MILL_REPO_URL/releases/latest" -MaximumRedirection 0 + } + catch { + $_.Exception.Response.Headers.Location.AbsoluteUri + } + + $targetUrl -match "^$MILL_REPO_URL/releases/tag/(.+)$" | Out-Null + + $MILL_VERSION = $Matches.1 + + if ($null -ne $MILL_VERSION) { + Set-Content -Path $MILL_LATEST_PATH -Value $MILL_VERSION + } + } + + if ($null -eq $MILL_VERSION) { + $MILL_VERSION = $DEFAULT_MILL_VERSION + Write-Warning "Falling back to hardcoded mill version $MILL_VERSION" + } + else { + Write-Output "Using mill version $MILL_VERSION" + } +} + +$MILL = "$MILL_DOWNLOAD_PATH/$MILL_VERSION.bat" + +if (-not (Test-Path -Path $MILL -PathType Leaf)) { + $DOWNLOAD_SUFFIX, $DOWNLOAD_FROM_MAVEN = switch -Regex ($MILL_VERSION) { + '^0\.[0-4]\..*$' { '', $false } + '0\.(?:[5-9]\.|10\.|11\.0-M).*' { '-assembly', $false } + Default { '-assembly', $true } + } + + if ($DOWNLOAD_FROM_MAVEN) { + $DOWNLOAD_URL = "https://repo1.maven.org/maven2/com/lihaoyi/mill-dist/$MILL_VERSION/mill-dist-$MILL_VERSION.jar" + } + else { + $MILL_VERSION -match '(\d+\.\d+\.\d+(?:-M\d+)?)' | Out-Null + $MILL_VERSION_TAG = $Matches.1 + $DOWNLOAD_URL = "$GITHUB_RELEASE_CDN$MILL_REPO_URL/releases/download/$MILL_VERSION_TAG/$MILL_VERSION$DOWNLOAD_SUFFIX" + } + Write-Output "Downloading mill $MILL_VERSION from $DOWNLOAD_URL ..." + + Invoke-WebRequest -Uri $DOWNLOAD_URL -OutFile $MILL +} + +$MILL_MAIN_CLI = $Env:MILL_MAIN_CLI ?? $PSCommandPath + +$MILL_FIRST_ARG = $null +$REMAINING_ARGUMENTS = $remainingArgs + +if ($null -ne $remainingArgs) { + if ($remainingArgs[0] -eq '--bsp' -or $remainingArgs -eq '-i' -or $remainingArgs -eq '--interactive' -or $remainingArgs -eq '--no-server') { + $MILL_FIRST_ARG = $remainingArgs[0] + $REMAINING_ARGUMENTS = Select-Object -InputObject $remainingArgs -Skip 1 + } +} + +& $MILL $MILL_FIRST_ARG -D "mill.main.cli=$MILL_MAIN_CLI" $REMAINING_ARGUMENTS diff --git a/package.json b/package.json new file mode 100644 index 00000000..bd3e636e --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "private": true, + "workspaces": [ + "cdk" + ], + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "jest", + "cdk": "cdk", + "setup-ide": "./mill mill.bsp.BSP/install" + }, + "devDependencies": { + "@types/jest": "^27.5.2", + "@types/node": "10.17.27", + "@types/prettier": "2.6.0", + "aws-cdk": "2.160.0", + "jest": "^27.5.1", + "ts-jest": "^27.1.4", + "ts-node": "^10.9.1", + "typescript": "~3.9.7" + }, + "dependencies": { + "aws-cdk-lib": "2.160.0", + "constructs": "^10.0.0" + } +} diff --git a/server/package.mill b/server/package.mill new file mode 100644 index 00000000..b31f8a38 --- /dev/null +++ b/server/package.mill @@ -0,0 +1,19 @@ + +package build.server +import mill._, scalalib._ +import $file.versions.V +import $file.util.BankerXScalaModule + +object `package` extends RootModule with BankerXScalaModule { + //def moduleDeps = Seq(build.bar.qux.mymodule) + def ivyDeps = Agg( + ivy"com.softwaremill.sttp.tapir::tapir-netty-server-sync::${V.Libs.tapir}", + ivy"com.softwaremill.sttp.tapir::tapir-jsoniter-scala:${V.Libs.tapir}", + ivy"com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:${V.Libs.tapir}", + ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-core:${V.Libs.`jsoniter-scala`}", + ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:${V.Libs.`jsoniter-scala`}", + ivy"com.softwaremill.ox::core:${V.Libs.ox}", + ) + + def moduleDeps = Seq(build.api) +} \ No newline at end of file diff --git a/server/src/bankerx/server/Main.scala b/server/src/bankerx/server/Main.scala new file mode 100644 index 00000000..fea4b754 --- /dev/null +++ b/server/src/bankerx/server/Main.scala @@ -0,0 +1,13 @@ +package bankerx.server + +import ox.* +import sttp.tapir.server.netty.sync.NettySyncServer + +object Main extends OxApp.Simple: + + def run(using Ox): Unit = + + val port = sys.env.get("HTTP_PORT").flatMap(_.toIntOption).getOrElse(8080) + val binding = useInScope(NettySyncServer().port(port).addEndpoints(ServerEndpoints.all).start())(_.stop()) + println(s"Server started at http://localhost:${binding.port}. ") + never \ No newline at end of file diff --git a/server/src/bankerx/server/ServerEndpoints.scala b/server/src/bankerx/server/ServerEndpoints.scala new file mode 100644 index 00000000..d5198a7f --- /dev/null +++ b/server/src/bankerx/server/ServerEndpoints.scala @@ -0,0 +1,21 @@ +package bankerx.server +import sttp.tapir.* +import sttp.shared.Identity +import sttp.tapir.generic.auto.* +import sttp.tapir.json.jsoniter.* +import sttp.tapir.server.ServerEndpoint +import sttp.tapir.swagger.bundle.SwaggerInterpreter +import bankerx.* +import bankerx.api.* + + +object ServerEndpoints: + val getTermsServerEndpoint = + PublicEndpoints.getTermsEndpoint.handle{ + case (bankName, purchase) => SmartWallet.getTerms(bankName)(purchase).toRight(s"Terms unavailable for bank: $bankName") + } + + val apiEndpoints = List(getTermsServerEndpoint) + val docEndpoints: List[ServerEndpoint[Any, Identity]] = SwaggerInterpreter() + .fromServerEndpoints[Identity](apiEndpoints, "bankerx", "1.0.0") + val all = apiEndpoints ++ docEndpoints diff --git a/serverless/package.mill b/serverless/package.mill new file mode 100644 index 00000000..88f41a33 --- /dev/null +++ b/serverless/package.mill @@ -0,0 +1,17 @@ + +package build.serverless +import mill._, scalalib._ +import $file.versions.V +import $file.util.BankerXScalaModule + +object `package` extends RootModule with BankerXScalaModule { + //def moduleDeps = Seq(build.bar.qux.mymodule) + def ivyDeps = Agg( + ivy"com.softwaremill.sttp.tapir::tapir-aws-lambda-zio::${V.Libs.tapir}", + ivy"dev.zio::zio::${V.Libs.zio}", + ivy"com.softwaremill.sttp.tapir::tapir-jsoniter-scala:${V.Libs.tapir}", + ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-core:${V.Libs.`jsoniter-scala`}", + ivy"com.github.plokhotnyuk.jsoniter-scala::jsoniter-scala-macros:${V.Libs.`jsoniter-scala`}", + ) + def moduleDeps = Seq(build.api) +} \ No newline at end of file diff --git a/serverless/src/bankerx/serverless/BankerXLambdaHandler.scala b/serverless/src/bankerx/serverless/BankerXLambdaHandler.scala new file mode 100644 index 00000000..bb8658e8 --- /dev/null +++ b/serverless/src/bankerx/serverless/BankerXLambdaHandler.scala @@ -0,0 +1,29 @@ +package bankerx.serverless + +import com.amazonaws.services.lambda.runtime.{Context, RequestStreamHandler} +import io.circe.generic.auto._ +import sttp.tapir.* +import sttp.tapir.server.ServerEndpoint +import sttp.tapir.serverless.aws.lambda.* +import sttp.tapir.serverless.aws.ziolambda.* +import sttp.tapir.ztapir.RIOMonadError +import java.io.{InputStream, OutputStream} +import zio.* + +object BankerXLambdaHandler extends RequestStreamHandler: + private given RIOMonadError[Any] = new RIOMonadError[Any] + private val handler = + ZioLambdaHandler.default[Any](ServerlessEndpoints.allEndpoints.toList) + + override def handleRequest( + input: InputStream, + output: OutputStream, + context: Context + ): Unit = + val runtime = Runtime.default + Unsafe.unsafe { implicit unsafe => + runtime.unsafe + .run(handler.process[AwsRequestV1](input, output)) + .getOrThrowFiberFailure() + + } diff --git a/serverless/src/bankerx/serverless/ServerlessEndpoints.scala b/serverless/src/bankerx/serverless/ServerlessEndpoints.scala new file mode 100644 index 00000000..eb48001a --- /dev/null +++ b/serverless/src/bankerx/serverless/ServerlessEndpoints.scala @@ -0,0 +1,27 @@ +package bankerx.serverless +import sttp.model.{Header, MediaType} +import sttp.tapir.* +import sttp.tapir.generic.auto.* +import sttp.tapir.json.jsoniter.* +import sttp.tapir.serverless.aws.lambda.{given, *} +import sttp.tapir.serverless.aws.ziolambda.{given, *} +import sttp.tapir.ztapir.ZTapir +import java.io.{InputStream, OutputStream} +import zio.{given, *} +import sttp.tapir.json.circe.{given, *} +import sttp.tapir.EndpointIO.annotations.jsonbody +import bankerx.* +import bankerx.api.* + +object ServerlessEndpoints extends ZTapir: + type ZioEndpoint = ZServerEndpoint[Any, Any] + val getTermsServerEndpoint: ZioEndpoint = + PublicEndpoints.getTermsEndpoint + .zServerLogic { case (bankName, purchase) => + val result = SmartWallet + .getTerms(bankName)(purchase) + .toRight(s"Terms unavailable for bank: $bankName") + ZIO.fromEither(result) + } + + val allEndpoints: Set[ZioEndpoint] = Set(getTermsServerEndpoint) diff --git a/util.mill b/util.mill new file mode 100644 index 00000000..ab11b0b2 --- /dev/null +++ b/util.mill @@ -0,0 +1,252 @@ +package build +import mill._, scalalib._ +import upickle.default.{ReadWriter => RW, macroRW} +import $file.versions.V + +trait BankerXScalaModule extends ScalaModule with CommonModule { + def scalaVersion = V.Scala.version +} + +trait MorphirScalaModule extends MorphirModule with ScalaModule { + def build = T { + compile() + } + + def resources = T.sources { + super.resources() ++ Seq(morphirResources()) + } + + def morphirScalaGenOutputDir = T { T.dest } + def morphirGeneratedScalaSources = T.source { morphirScalaGen()} + def morphirAllGeneratedScalaSourceFiles = T { + + os.walk(morphirGeneratedScalaSources().path) + .filter(_.ext == "scala") + .toSeq + } + + /// Generates Scala sources from Morphir IR + def morphirScalaGen = T { + val irPath = morphirMakeGeneratedIR().path + val outputDir = morphirScalaGenOutputDir() + os.proc("npx", "morphir", "scala-gen", + "--input", irPath.toString, + "--output", outputDir.toString, + "--target", "Scala" + ).call(cwd = millSourcePath) + PathRef(outputDir) + } + + /// The location of generated sources + override def generatedSources = T { + super.generatedSources() ++ Seq(morphirGeneratedScalaSources()) + } +} + +trait MorphirModule extends NodeModule { + def morphirProjectFilename = T { "morphir.json" } + def morphirProjectDir = T { millSourcePath } + def morphirProjectSource = T { PathRef(morphirProjectDir() / morphirProjectFilename()) } + def morphirMakeOutputDir = T {T.dest} + def morphirIRFileName = "morphir-ir.json" + def morphirMakeGeneratedIR = T { + val makeResult = morphirMake() + val ir = makeResult("MorphirIR").path + PathRef(ir) + } + + def morphirResources = T.source { + val dest = T.dest + val makeResult = morphirMakeGeneratedIR() + val outputDir = dest / ".morphir" + os.makeDir.all(outputDir) + if(os.exists(makeResult.path)) + os.copy.into(makeResult.path, outputDir) + PathRef(dest) + } + + def morphirSources = T.sources { + val project = morphirProjectSource().path + val projectDir = morphirProjectDir() + if(os.exists(project)){ + val projectJson = ujson.read(os.read(project)) + val sourceDirectory = projectJson("sourceDirectory").str + val sourceDir = os.FilePath(sourceDirectory) + //T.log.info(s"Source directory: ${sourceDir}") + val resolvedPath = sourceDir match { + case rel: os.RelPath => projectDir / rel + case sub: os.SubPath => projectDir / sub + case abs: os.Path => abs + case _ => throw new Exception(s"Invalid source directory path: ${sourceDir}") + } + Seq(PathRef(resolvedPath)) + } else { + throw new Exception(s"Missing Morphir project file: the project was not found at the expected location ${project}.") + } + } + + def allMorphirSourceFiles = T { + Lib.findSourceFiles(morphirSources(), Seq("elm")).map(PathRef(_)) + } + + def morphirMake = T { + val _ = npmInstall() + //shell("npx", "morphir", "make").call(osName = osName(), cwd = millSourcePath) + + // Needed for incremental build/input tracking + val _ = morphirProjectSource().path + val _ = allMorphirSourceFiles() + + val projectDir = morphirProjectDir() + val outputDir = morphirMakeOutputDir() + val output = outputDir / morphirIRFileName + os.proc("npx", "morphir", "make", "--project-dir", projectDir.toString, "--output", output.toString).call(cwd = millSourcePath) + Map( + "MorphirIR" -> PathRef(output), + "Hashes" -> PathRef(projectDir / "morphir-hashes.json") + ) + } +} + +trait CommonModule extends Module { + def osName = T.input { OsName.default } + + def isWindows = T { osName().isWindows } + def isMac = T { osName().isMac } + +} + +trait NodeModule extends CommonModule { + def nodeProjectRoot = T { millSourcePath } + + def packageJsonSource = T { + PathRef(nodeProjectRoot() / "package.json") + } + + def packageLockJsonSource = T { + PathRef(nodeProjectRoot() / "package-lock.json") + } + + def npmInstall = T { + val workingDir = nodeProjectRoot() + val _ = packageJsonSource().path + val _ = packageLockJsonSource().path + npmInstallAction(workingDir = workingDir, osName = osName()) + } + + def npmInstallAction(workingDir:os.Path, osName:OsName = OsName.default) = { + //shell("npm", "install").call(osName = osName, cwd = workingDir) + os.proc("npm", "install").call(cwd = workingDir) + Map( + "nodeModulesDir" -> PathRef(workingDir / "node_modules") , + "workingDir" -> PathRef(workingDir) + ) + } +} + +case class shell(command:os.Shellable*) { + import shell._ + def call( + cwd:os.Path = null, + env:Map[String, String] = Map.empty, + stdin:os.ProcessInput = os.Pipe, + stdout:os.ProcessOutput = os.Pipe, + osName:OsName = OsName.default, + shellConfig:shell.ShellConfig = shell.ShellConfig.default + ) = { + val cmdPrefix = commandPrefix(osName, shellConfig) + os.proc(cmdPrefix:_*).call(cwd = cwd, env = env, stdin = stdin, stdout = stdout) + } + + def commandPrefix(osName:OsName, shellConfig:ShellConfig):Seq[os.Shellable] = { + osName match { + case OsName.Windows(_) => shellConfig.windowsShellPrefix + case OsName.Mac(_) => shellConfig.macShellPrefix + case OsName.Linux(_) => shellConfig.linuxShellPrefix + case OsName.Unknown(_) => shellConfig.unknownShellPrefix + } + } +} + +object shell { + val defaultWindowsShellPrefix:Seq[os.Shellable] = Seq("cmd.exe", "/c") + val defaultMacShellPrefix:Seq[os.Shellable] = Seq("/bin/sh", "-c") + val defaultLinuxShellPrefix:Seq[os.Shellable] = Seq("/bin/sh", "-c") + val defaultUnknownShellPrefix:Seq[os.Shellable] = Seq("/bin/sh", "-c") + + final case class ShellConfig( + windowsShellPrefix:Seq[os.Shellable] = defaultWindowsShellPrefix, + macShellPrefix:Seq[os.Shellable] = defaultMacShellPrefix, + linuxShellPrefix:Seq[os.Shellable] = defaultLinuxShellPrefix, + unknownShellPrefix:Seq[os.Shellable] = defaultUnknownShellPrefix + ) + + object ShellConfig { + val default = ShellConfig() + } +} + +sealed trait OsName extends Product with Serializable { self => + import OsName._ + val rawName:String + def isWindows:Boolean = self match { + case Windows(_) => true + case _ => false + } + + def isMac:Boolean = self match { + case Mac(_) => true + case _ => false + } + def isLinux:Boolean = self match { + case Linux(_) => true + case _ => false + } + def isUnknown:Boolean = self match { + case Unknown(_) => true + case _ => false + } +} + +object OsName { + import scala.util.matching.Regex + implicit val readWriter:RW[OsName] = RW.merge(Windows.readWriter, Mac.readWriter, Linux.readWriter, Unknown.readWriter) + + lazy val default = OsName.parse(System.getProperty("os.name")) + val windowsOsNamePattern = "(?i)windows".r + val macOsNamePattern = "(?i)darwin|mac".r + val linuxOsNamePattern = "(?i)linux".r + + @inline def apply(input:String):OsName = parse(input) + + def parse(input:String):OsName = { + if (windowsOsNamePattern.findFirstIn(input).isDefined) Windows(input) + else if (macOsNamePattern.findFirstIn(input).isDefined) Mac(input) + else if (linuxOsNamePattern.findFirstIn(input).isDefined) Linux(input) + else Unknown(input) + } + + def windows(rawName:String):OsName = Windows(rawName) + def mac(rawName:String):OsName = Mac(rawName) + def linux(rawName:String):OsName = Linux(rawName) + def unknown(rawName:String):OsName = Unknown(rawName) + + final case class Windows private(rawName:String) extends OsName + object Windows { + implicit val readWriter:RW[Windows] = macroRW + } + final case class Mac private(rawName:String) extends OsName + object Mac { + implicit val readWriter:RW[Mac] = macroRW + } + final case class Linux private(rawName:String) extends OsName + object Linux { + implicit val readWriter:RW[Linux] = macroRW + } + + final case class Unknown private(rawName:String) extends OsName + object Unknown { + implicit val readWriter:RW[Unknown] = macroRW + val default = Unknown("unknown") + } +} \ No newline at end of file diff --git a/versions.mill b/versions.mill new file mode 100644 index 00000000..2db4efad --- /dev/null +++ b/versions.mill @@ -0,0 +1,19 @@ +package build +import mill._, scalalib._ + +object V { + object Libs { + val `case-app` = "2.1.0-M29" + val `cats-effect` = "3.5.4" + val `jsoniter-scala` = "2.30.11" + val `morphir-jvm` = "0.18.6" + val pprint = "0.9.0" + val scalatest = "3.2.19" + val tapir = "1.11.5" + val ox = "0.4.0" + val zio = "2.1.9" + } + object Scala { + val version = "3.5.0" + } +} \ No newline at end of file