diff --git a/build.sbt b/build.sbt index 5163e312..19a880ed 100644 --- a/build.sbt +++ b/build.sbt @@ -185,7 +185,6 @@ lazy val vercelNodeJS = project ) lazy val examples = crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) .in(file("examples")) .settings( libraryDependencies ++= Seq( @@ -198,6 +197,12 @@ lazy val examples = crossProject(JSPlatform, JVMPlatform) ) .settings(commonSettings) .dependsOn(lambda, lambdaHttp4s) + .jsConfigure(_.dependsOn(vercelNodeJS)) + .jsSettings( + scalaJSUseMainModuleInitializer := true, + Compile / mainClass := Some("feral.examples.vercelNodejsHandler"), + scalaJSLinkerConfig ~= { _.withModuleKind(ModuleKind.CommonJSModule) } + ) .enablePlugins(NoPublishPlugin) lazy val unidocs = project @@ -212,8 +217,7 @@ lazy val unidocs = project inProjects( lambda.jvm, lambdaHttp4s.jvm, - lambdaCloudFormationCustomResource.jvm, - vercelNodeJS + lambdaCloudFormationCustomResource.jvm ) } ) diff --git a/examples/js/src/main/scala/feral/examples/VercelNodejs.scala b/examples/js/src/main/scala/feral/examples/VercelNodejs.scala new file mode 100644 index 00000000..45b7d54f --- /dev/null +++ b/examples/js/src/main/scala/feral/examples/VercelNodejs.scala @@ -0,0 +1,38 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package feral.examples + +import cats.effect._ +import feral.vercel._ +import org.http4s._ +import org.http4s.dsl.io._ + +object vercelNodejsHandler extends IOVercel { + def handler = { + val app = HttpRoutes + .of[IO] { + case GET -> Root / "hello" / name => + Ok(s"Hello, $name.") + } + .orNotFound + + Resource.pure(app) + + /*Resource.pure(HttpApp.pure(Response[IO](Status.Ok)))*/ + } + +} diff --git a/examples/src/main/scala/feral/examples/Http4sLambda.scala b/examples/shared/src/main/scala/feral/examples/Http4sLambda.scala similarity index 100% rename from examples/src/main/scala/feral/examples/Http4sLambda.scala rename to examples/shared/src/main/scala/feral/examples/Http4sLambda.scala diff --git a/examples/src/main/scala/feral/examples/KinesisLambda.scala b/examples/shared/src/main/scala/feral/examples/KinesisLambda.scala similarity index 100% rename from examples/src/main/scala/feral/examples/KinesisLambda.scala rename to examples/shared/src/main/scala/feral/examples/KinesisLambda.scala diff --git a/vercel-nodejs/src/main/scala/feral/vercel/IOVercel.scala b/vercel-nodejs/src/main/scala/feral/vercel/IOVercel.scala new file mode 100644 index 00000000..3a3c23ac --- /dev/null +++ b/vercel-nodejs/src/main/scala/feral/vercel/IOVercel.scala @@ -0,0 +1,57 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package feral.vercel + +import cats.effect.IO +import cats.effect.kernel.Resource +import cats.effect.std.Dispatcher +import cats.effect.unsafe.IORuntime +import cats.syntax.all._ +import org.http4s.HttpApp +import org.http4s.nodejs.IncomingMessage +import org.http4s.nodejs.ServerResponse + +import scala.scalajs.js + +abstract class IOVercel { + + final def main(args: Array[String]): Unit = + js.Dynamic.global.module.exports = handlerFn + + protected def runtime: IORuntime = IORuntime.global + + def handler: Resource[IO, HttpApp[IO]] + + private[vercel] lazy val handlerFn: js.Function2[IncomingMessage, ServerResponse, Unit] = { + val dispatcherHandle = { + Dispatcher + .parallel[IO](await = false) + .product(handler) + .allocated + .map(_._1) // drop unused finalizer + .unsafeToPromise()(runtime) + } + + (request: IncomingMessage, response: ServerResponse) => + val _ = dispatcherHandle.`then`[Unit] { + case (dispatcher, handle) => + dispatcher.unsafeRunAndForget( + request.toRequest[IO].flatMap(handle(_)).flatMap(response.writeResponse[IO]) + ) + } + } +}