Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
0c71749
Migrate Node.js Stream interop to handmade facade
armanbilge May 15, 2022
ae332a4
Bug fixes
armanbilge May 15, 2022
574d79c
More bug fixes
armanbilge May 15, 2022
83a7685
One more fix
armanbilge May 16, 2022
304408f
Organize imports
armanbilge May 16, 2022
bd5b39f
Header
armanbilge May 16, 2022
56fdaea
Handmade facade for stdin/stdout/stderr
armanbilge May 16, 2022
8c45904
Organize imports
armanbilge May 16, 2022
ca16e26
Move UnixSockets to handmade facade
armanbilge May 16, 2022
1f5d3b3
Port Socket to handmade facade
armanbilge May 16, 2022
7ba9e3a
Port SocketOption to handmade facade
armanbilge May 16, 2022
6b4d865
Fix warning
armanbilge May 16, 2022
c872828
Port SocketGroup to handmade facade
armanbilge May 16, 2022
bbe322b
Port TLSSocket to handmade facade
armanbilge May 16, 2022
b1b522a
WIP porting TLS to handmade facade
armanbilge May 16, 2022
bb6330c
Port `SecureContext` to handmade facade
armanbilge May 29, 2022
47d7e5e
Finish porting TLS
armanbilge May 29, 2022
f07e072
Don't explicitly set undefined
armanbilge May 29, 2022
7e007a9
Fix `@JSImport`
armanbilge May 29, 2022
b531135
Migrate path to handmade facade
armanbilge May 29, 2022
936c759
Filename bikeshedding
armanbilge May 29, 2022
fe062a4
fs2.io.file.js on handmade facade
armanbilge May 29, 2022
a72761e
Bug fix
armanbilge May 29, 2022
c7ae0a7
Use handmade facade for tests
armanbilge May 29, 2022
ea3bc7d
Remove more uses of old facade
armanbilge May 29, 2022
57213d9
Formatting
armanbilge May 29, 2022
501872a
Fix Scala 3 compile
armanbilge Jun 5, 2022
f1209c1
Reorganize facade in packages
armanbilge Jun 5, 2022
a1b50a1
Port UDP to handmade facade
armanbilge Jun 5, 2022
0d3d406
Fixes
armanbilge Jun 5, 2022
c0e8144
Port compression to handmade facade
armanbilge Jun 5, 2022
5e94ff6
Port compression suite
armanbilge Jun 5, 2022
3b5bfa1
Remove unused helpers
armanbilge Jun 5, 2022
58c9fe7
Remove last uses of npm deps
armanbilge Jun 5, 2022
5e3b2f2
Remove lockfiles, ScalablyTyped, Bundler
armanbilge Jun 5, 2022
9718757
streams are their own module, too
armanbilge Jun 5, 2022
7862a69
Regenerate workflow
armanbilge Jun 5, 2022
8e76349
MiMa filters
armanbilge Jun 5, 2022
4fd54fc
More filters
armanbilge Jun 5, 2022
59edb7f
Merge branch 'main' into topic/node-facade
mpilquist Jun 8, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,11 @@ jobs:

- name: Make target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/series/2.5.x')
run: mkdir -p target node/js/target protocols/js/target unidocs/target .js/target core/js/target mdoc/target core/jvm/target .jvm/target .native/target scodec/jvm/target scodec/js/target io/js/target reactive-streams/target io/jvm/target protocols/jvm/target benchmark/target project/target
run: mkdir -p target protocols/js/target unidocs/target .js/target core/js/target mdoc/target core/jvm/target .jvm/target .native/target scodec/jvm/target scodec/js/target io/js/target reactive-streams/target io/jvm/target protocols/jvm/target benchmark/target project/target

- name: Compress target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/series/2.5.x')
run: tar cf targets.tar target node/js/target protocols/js/target unidocs/target .js/target core/js/target mdoc/target core/jvm/target .jvm/target .native/target scodec/jvm/target scodec/js/target io/js/target reactive-streams/target io/jvm/target protocols/jvm/target benchmark/target project/target
run: tar cf targets.tar target protocols/js/target unidocs/target .js/target core/js/target mdoc/target core/jvm/target .jvm/target .native/target scodec/jvm/target scodec/js/target io/js/target reactive-streams/target io/jvm/target protocols/jvm/target benchmark/target project/target

- name: Upload target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/series/2.5.x')
Expand Down
60 changes: 32 additions & 28 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import com.typesafe.tools.mima.core._

Global / onChangedBuildSource := ReloadOnSourceChanges
Global / stQuiet := true

ThisBuild / tlBaseVersion := "3.2"

Expand Down Expand Up @@ -166,7 +165,6 @@ lazy val root = tlCrossRootProject
.aggregate(
core,
io,
node,
scodec,
protocols,
reactiveStreams,
Expand Down Expand Up @@ -217,29 +215,8 @@ lazy val coreJS = core.js
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule))
)

lazy val node = crossProject(JSPlatform)
.in(file("node"))
.enablePlugins(ScalablyTypedConverterGenSourcePlugin)
.disablePlugins(DoctestPlugin)
.settings(
name := "fs2-node",
mimaPreviousArtifacts := Set.empty,
scalacOptions += "-nowarn",
Compile / doc / sources := Nil,
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule)),
Compile / npmDevDependencies += "@types/node" -> "16.11.7",
useYarn := true,
yarnExtraArgs += "--frozen-lockfile",
stOutputPackage := "fs2.internal.jsdeps",
stPrivateWithin := Some("fs2"),
stStdlib := List("es2020"),
stUseScalaJsDom := false,
stIncludeDev := true
)

lazy val io = crossProject(JVMPlatform, JSPlatform)
.in(file("io"))
.jsEnablePlugins(ScalaJSBundlerPlugin)
.settings(
name := "fs2-io",
libraryDependencies += "com.comcast" %%% "ip4s-core" % "3.1.3",
Expand All @@ -254,13 +231,9 @@ lazy val io = crossProject(JVMPlatform, JSPlatform)
)
.jsSettings(
tlVersionIntroduced := List("2.12", "2.13", "3").map(_ -> "3.1.0").toMap,
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule)),
Test / npmDevDependencies += "jks-js" -> "1.0.1",
useYarn := true,
yarnExtraArgs += "--frozen-lockfile"
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule))
)
.dependsOn(core % "compile->compile;test->test")
.jsConfigure(_.dependsOn(node.js))
.jsSettings(
mimaBinaryIssueFilters ++= Seq(
ProblemFilters.exclude[IncompatibleMethTypeProblem]("fs2.io.package.stdinUtf8"),
Expand All @@ -273,6 +246,37 @@ lazy val io = crossProject(JVMPlatform, JSPlatform)
ProblemFilters.exclude[MissingClassProblem]("fs2.io.net.JavaScriptUnknownException"),
ProblemFilters.exclude[DirectMissingMethodProblem](
"fs2.io.net.tls.TLSSocketCompanionPlatform#AsyncTLSSocket.this"
),
ProblemFilters.exclude[IncompatibleMethTypeProblem]("fs2.io.file.FileHandle.make"),
ProblemFilters.exclude[IncompatibleMethTypeProblem]("fs2.io.net.DatagramSocket.forAsync"),
ProblemFilters.exclude[IncompatibleMethTypeProblem](
"fs2.io.net.DatagramSocketCompanionPlatform#AsyncDatagramSocket.this"
),
ProblemFilters
.exclude[ReversedMissingMethodProblem]("fs2.io.net.DatagramSocketOption#Key.set"),
ProblemFilters.exclude[IncompatibleMethTypeProblem]("fs2.io.net.Socket.forAsync"),
ProblemFilters.exclude[IncompatibleMethTypeProblem]("fs2.io.net.SocketOption.encoding"),
ProblemFilters
.exclude[ReversedMissingMethodProblem]("fs2.io.net.SocketOptionCompanionPlatform#Key.set"),
ProblemFilters
.exclude[ReversedMissingMethodProblem]("fs2.io.net.tls.SecureContext#SecureVersion.toJS"),
ProblemFilters.exclude[MissingClassProblem]("fs2.io.net.tls.SecureContext$ops"),
ProblemFilters.exclude[Problem]("fs2.io.net.tls.SecureContext.ops"),
ProblemFilters.exclude[MissingClassProblem]("fs2.io.net.tls.SecureContext$ops$"),
ProblemFilters.exclude[IncompatibleResultTypeProblem](
"fs2.io.net.tls.TLSParameters#DefaultTLSParameters.toTLSSocketOptions"
),
ProblemFilters.exclude[DirectMissingMethodProblem](
"fs2.io.net.tls.TLSParameters#DefaultTLSParameters.toConnectionOptions"
),
ProblemFilters.exclude[IncompatibleResultTypeProblem](
"fs2.io.net.tls.SecureContext#SecureVersion#TLSv1.1.toJS"
),
ProblemFilters.exclude[IncompatibleResultTypeProblem](
"fs2.io.net.tls.SecureContext#SecureVersion#TLSv1.2.toJS"
),
ProblemFilters.exclude[IncompatibleResultTypeProblem](
"fs2.io.net.tls.SecureContext#SecureVersion#TLSv1.3.toJS"
)
)
)
Expand Down
38 changes: 35 additions & 3 deletions io/js/src/main/scala/fs2/io/NodeStream.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,56 @@

package fs2.io

import fs2.io.internal.facade.events.EventEmitter

import scala.annotation.nowarn
import scala.scalajs.js

/** A facade for Node.js `stream.Readable`. Extend or cast to/from your own bindings.
* @see [[https://nodejs.org/api/stream.html]]
*/
@js.native
trait Readable extends js.Object
@nowarn
trait Readable extends EventEmitter {

protected[io] def read(): js.typedarray.Uint8Array = js.native

protected[io] def destroy(): this.type = js.native

protected[io] def destroy(error: js.Error): this.type = js.native

protected[io] def push(chunk: js.typedarray.Uint8Array): Boolean = js.native

protected[io] def readableEnded: Boolean = js.native

}

/** A facade for Node.js `stream.Writable`. Extend or cast to/from your own bindings.
* @see [[https://nodejs.org/api/stream.html]]
*/
@js.native
trait Writable extends js.Object
@nowarn
trait Writable extends EventEmitter {

protected[io] def destroy(error: js.Error): this.type = js.native

protected[io] def write(
chunk: js.typedarray.Uint8Array,
cb: js.Function1[js.UndefOr[js.Error], Unit]
): Boolean = js.native

protected[io] def end(cb: js.Function1[js.UndefOr[js.Error], Unit]): this.type = js.native

protected[io] def writableEnded: Boolean = js.native

}

/** A facade for Node.js `stream.Duplex`. Extend or cast to/from your own bindings.
* @see [[https://nodejs.org/api/stream.html]]
*/
@js.native
trait Duplex extends js.Object with Readable with Writable
trait Duplex extends Readable with Writable {
protected[io] override def destroy(error: js.Error): this.type = js.native
}

final class StreamDestroyedException private[io] () extends IOException
37 changes: 19 additions & 18 deletions io/js/src/main/scala/fs2/io/compressionplatform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,53 +28,54 @@ import fs2.compression.Compression
import fs2.compression.DeflateParams
import fs2.compression.InflateParams
import fs2.compression.ZLibParams
import fs2.internal.jsdeps.node.zlibMod
import fs2.io.internal.facade

private[io] trait compressionplatform {

implicit def fs2ioCompressionForAsync[F[_]](implicit F: Async[F]): Compression[F] =
new Compression.UnsealedCompression[F] {

override def deflate(deflateParams: DeflateParams): Pipe[F, Byte, Byte] = in => {
val options = zlibMod
.ZlibOptions()
.setChunkSize(deflateParams.bufferSizeOrMinimum.toDouble)
.setLevel(deflateParams.level.juzDeflaterLevel.toDouble)
.setStrategy(deflateParams.strategy.juzDeflaterStrategy.toDouble)
.setFlush(deflateParams.flushMode.juzDeflaterFlushMode.toDouble)
val options = new facade.zlib.Options {
chunkSize = deflateParams.bufferSizeOrMinimum
level = deflateParams.level.juzDeflaterLevel
strategy = deflateParams.strategy.juzDeflaterStrategy
flush = deflateParams.flushMode.juzDeflaterFlushMode
}

Stream
.resource(suspendReadableAndRead() {
(deflateParams.header match {
case ZLibParams.Header.GZIP => zlibMod.createGzip(options)
case ZLibParams.Header.ZLIB => zlibMod.createDeflate(options)
}).asInstanceOf[Duplex]
case ZLibParams.Header.GZIP => facade.zlib.createGzip(options)
case ZLibParams.Header.ZLIB => facade.zlib.createDeflate(options)
})
})
.flatMap { case (deflate, out) =>
out
.concurrently(in.through(writeWritable[F](deflate.pure.widen)))
.onFinalize(
F.async_[Unit](cb => deflate.asInstanceOf[zlibMod.Zlib].close(() => cb(Right(()))))
F.async_[Unit](cb => deflate.close(() => cb(Right(()))))
)
}
}

override def inflate(inflateParams: InflateParams): Pipe[F, Byte, Byte] = in => {
val options = zlibMod
.ZlibOptions()
.setChunkSize(inflateParams.bufferSizeOrMinimum.toDouble)
val options = new facade.zlib.Options {
chunkSize = inflateParams.bufferSizeOrMinimum
}

Stream
.resource(suspendReadableAndRead() {
(inflateParams.header match {
case ZLibParams.Header.GZIP => zlibMod.createGunzip(options)
case ZLibParams.Header.ZLIB => zlibMod.createInflate(options)
}).asInstanceOf[Duplex]
case ZLibParams.Header.GZIP => facade.zlib.createGunzip(options)
case ZLibParams.Header.ZLIB => facade.zlib.createInflate(options)
})
})
.flatMap { case (inflate, out) =>
out
.concurrently(in.through(writeWritable[F](inflate.pure.widen)))
.onFinalize(
F.async_[Unit](cb => inflate.asInstanceOf[zlibMod.Zlib].close(() => cb(Right(()))))
F.async_[Unit](cb => inflate.close(() => cb(Right(()))))
)
}
}
Expand Down
8 changes: 4 additions & 4 deletions io/js/src/main/scala/fs2/io/file/CopyFlag.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ package io
package file

import cats.kernel.Monoid
import fs2.internal.jsdeps.node.fsMod
import fs2.io.internal.facade

final class CopyFlag private (private val bits: Long) extends AnyVal {
def jsBits: Long = bits ^ CopyFlag.ReplaceExisting.bits // Toggle the inverted bit
Expand All @@ -35,10 +35,10 @@ object CopyFlag extends CopyFlagCompanionApi {
private def apply(bits: Double): CopyFlag = CopyFlag(bits.toLong)

val ReplaceExisting = CopyFlag(
fsMod.constants.COPYFILE_EXCL
facade.fs.constants.COPYFILE_EXCL
) // Reuse this bit with inverted semantics
val Reflink = CopyFlag(fsMod.constants.COPYFILE_FICLONE)
val ReflinkOrFail = CopyFlag(fsMod.constants.COPYFILE_FICLONE_FORCE)
val Reflink = CopyFlag(facade.fs.constants.COPYFILE_FICLONE)
val ReflinkOrFail = CopyFlag(facade.fs.constants.COPYFILE_FICLONE_FORCE)

private[file] implicit val monoid: Monoid[CopyFlag] = new Monoid[CopyFlag] {
override def combine(x: CopyFlag, y: CopyFlag): CopyFlag = CopyFlag(x.bits | y.bits)
Expand Down
10 changes: 5 additions & 5 deletions io/js/src/main/scala/fs2/io/file/FileHandlePlatform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ package file

import cats.effect.kernel.Async
import cats.syntax.all._
import fs2.internal.jsdeps.node.fsPromisesMod
import fs2.io.internal.facade

import scala.scalajs.js.typedarray.Uint8Array

private[file] trait FileHandlePlatform[F[_]]

private[file] trait FileHandleCompanionPlatform {
private[file] def make[F[_]](
fd: fsPromisesMod.FileHandle
fd: facade.fs.FileHandle
)(implicit F: Async[F]): FileHandle[F] =
new FileHandle[F] {

Expand All @@ -42,12 +42,12 @@ private[file] trait FileHandleCompanionPlatform {

override def read(numBytes: Int, offset: Long): F[Option[Chunk[Byte]]] =
F.fromPromise(
F.delay(fd.read(new Uint8Array(numBytes), 0, numBytes.toDouble, offset.toDouble))
F.delay(fd.read(new Uint8Array(numBytes), 0, numBytes, offset.toDouble))
).map { res =>
if (res.bytesRead < 0) None
else if (res.bytesRead == 0) Some(Chunk.empty)
else
Some(Chunk.uint8Array(res.buffer).take(res.bytesRead.toInt))
Some(Chunk.uint8Array(res.buffer).take(res.bytesRead))
}

override def size: F[Long] =
Expand All @@ -58,7 +58,7 @@ private[file] trait FileHandleCompanionPlatform {

override def write(bytes: Chunk[Byte], offset: Long): F[Int] =
F.fromPromise(
F.delay(fd.write(bytes.toUint8Array, 0, bytes.size.toDouble, offset.toDouble))
F.delay(fd.write(bytes.toUint8Array, 0, bytes.size, offset.toDouble))
).map(_.bytesWritten.toInt)
}
}
Loading