Skip to content

Add WriteableStream support #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Notable changes to this project are documented in this file. The format is based
Breaking changes:

New features:
- Add WriteableStream support (#9)

Bugfixes:

Expand Down
24 changes: 24 additions & 0 deletions src/Web/Streams/Sink.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export function _make(options) {
var newOptions = {};
if (options.start) {
newOptions.start = function(controller) {
return options.start(controller)();
};
}
if (options.write) {
newOptions.write = function(chunk, controller) {
return options.write(chunk)(controller)();
};
}
if (options.close) {
newOptions.close = function(controller) {
return options.close(controller)();
};
}
if (options.abort) {
newOptions.abort = function(reason) {
return options.abort(reason)();
};
}
return newOptions;
}
26 changes: 26 additions & 0 deletions src/Web/Streams/Sink.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module Web.Streams.Sink
( Sink
, Optional
, make
) where

import Effect (Effect)
import Effect.Exception (Error)
import Prelude (Unit)
import Prim.Row as Row
import Web.Promise (Promise)
import Web.Streams.WritableStreamController (WritableStreamController)

type Optional chunk =
( start :: WritableStreamController chunk -> Effect (Promise Unit)
, write :: chunk -> WritableStreamController chunk -> Effect (Promise Unit)
, close :: WritableStreamController chunk -> Effect (Promise Unit)
, abort :: Error -> Effect (Promise Unit)
)

foreign import data Sink :: Type -> Type

make :: forall r rx chunk. Row.Union r rx (Optional chunk) => { | r } -> Sink chunk
make = _make

foreign import _make :: forall r chunk. { | r } -> Sink chunk
33 changes: 33 additions & 0 deletions src/Web/Streams/WritableStream.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export function _new(source, strategy) {
return new WritableStream(source, strategy);
}

export function close(stream) {
return function() {
return stream.close();
};
}

export function _abortErr(stream, reason) {
return function() {
return stream.abort(reason);
};
}

export function abort(stream) {
return function() {
return stream.abort();
};
}

export function locked(stream) {
return function() {
return stream.locked;
};
}

export function getWriter(stream) {
return function() {
return stream.getWriter();
};
}
40 changes: 40 additions & 0 deletions src/Web/Streams/WritableStream.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
module Web.Streams.WritableStream
( WritableStream
, new
, abort
, abortErr
, close
, getWriter
, locked
) where

import Data.Maybe (Maybe)
import Data.Nullable (Nullable, toNullable)
import Effect (Effect)
import Effect.Exception (Error)
import Effect.Uncurried (EffectFn2, runEffectFn2)
import Prelude (Unit)
import Web.Promise (Promise)
import Web.Streams.QueuingStrategy (QueuingStrategy)
import Web.Streams.Sink (Sink)
import Web.Streams.Writer (Writer)

foreign import data WritableStream :: Type -> Type

foreign import _new :: forall chunk. EffectFn2 (Nullable (Sink chunk)) (Nullable (QueuingStrategy chunk)) (WritableStream chunk)

new :: forall chunk. Maybe (Sink chunk) -> Maybe (QueuingStrategy chunk) -> Effect (WritableStream chunk)
new source strategy = runEffectFn2 _new (toNullable source) (toNullable strategy)

foreign import _abortErr :: forall chunk. EffectFn2 (WritableStream chunk) Error (Promise Error)

abortErr :: forall chunk. WritableStream chunk -> Error -> Effect (Promise Error)
abortErr = runEffectFn2 _abortErr

foreign import abort :: forall chunk. WritableStream chunk -> Effect (Promise Unit)

foreign import close :: forall chunk. WritableStream chunk -> Effect (Promise Unit)

foreign import getWriter :: forall chunk. WritableStream chunk -> Effect (Writer chunk)

foreign import locked :: forall chunk. WritableStream chunk -> Effect Boolean
7 changes: 7 additions & 0 deletions src/Web/Streams/WritableStreamController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function error(error) {
return function(controller) {
return function() {
return controller.error(error);
};
};
}
9 changes: 9 additions & 0 deletions src/Web/Streams/WritableStreamController.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Web.Streams.WritableStreamController where

import Effect (Effect)
import Effect.Exception (Error)
import Prelude (Unit)

foreign import data WritableStreamController :: Type -> Type

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the readonly attribute signal is missing here. If it's not added in this PR, we should open an issue tracking that.

Copy link
Author

@YourFin YourFin Jul 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my other comment on filling out the spec

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah; it appears that the returned AbortSignal type is currently defined in the web-fetch package, and it needs be moved to somewhere more centralized to avoid a circular dependency. A new "web-abort-signal" package seems somewhat overkill, but given its inclusion in the DOM standard rather than the fetch or stream standard I'm not sure what the best course of action would be. I'll leave this out of the next revision for now.

foreign import error :: forall chunk. WritableStreamController chunk -> Error -> Effect Unit
47 changes: 47 additions & 0 deletions src/Web/Streams/Writer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
export function _write(writer, chunk) {
return function() {
return writer.write(chunk);
};
}

export function close(writer) {
return function() {
return writer.close();
};
}

export function closed(writer) {
return function() {
return writer.closed;
};
}

export function _desiredSize(writer) {
return function() {
return writer.desiredSize;
};
}

export function ready(writer) {
return function() {
return writer.ready;
};
}

export function releaseLock(writer) {
return function() {
return writer.releaseLock();
};
}

export function abort(writer) {
return function() {
return writer.abort();
};
}

export function _abortErr(writer, err) {
return function() {
return writer.abort(err);
};
}
47 changes: 47 additions & 0 deletions src/Web/Streams/Writer.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module Web.Streams.Writer
( Writer
, abort
, abortErr
, close
, closed
, desiredSize
, ready
, write
) where

import Prelude

import Data.Maybe (Maybe)
import Data.Nullable (Nullable)
import Data.Nullable as Nullable
import Effect (Effect)
import Effect.Exception (Error)
import Effect.Uncurried (EffectFn2, runEffectFn2)
import Web.Promise (Promise)

foreign import data Writer :: Type -> Type

foreign import _write :: forall chunk. EffectFn2 (Writer chunk) chunk (Promise Unit)

write :: forall chunk. Writer chunk -> chunk -> Effect (Promise Unit)
write = runEffectFn2 _write

foreign import ready :: forall chunk. Writer chunk -> Effect (Promise Unit)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

closed, desiredSize, abort, and releaseLock are all not implemented here. If you don't want to add those now, could you open an issue after this PR is merged to track that?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy to add them; do we want to backfill the full spec on Reader too? I noticed that the implementation here is missing a few methods/properties.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean https://streams.spec.whatwg.org/#default-reader-class? Perhaps as a separate PR.

foreign import close :: forall chunk. Writer chunk -> Effect (Promise Unit)

foreign import closed :: forall chunk. Writer chunk -> Effect (Promise Unit)

foreign import _desiredSize :: forall chunk. Writer chunk -> Effect (Nullable Int)

desiredSize :: forall chunk. Writer chunk -> Effect (Maybe Int)
desiredSize = map (Nullable.toMaybe) <<< _desiredSize

foreign import releaseLock :: forall chunk. Writer chunk -> Effect Unit

foreign import abort :: forall chunk. Writer chunk -> Promise (Effect Unit)

foreign import _abortErr :: forall chunk. EffectFn2 (Writer chunk) Error (Promise Error)

abortErr :: forall chunk. Writer chunk -> Error -> Effect (Promise Error)
abortErr = runEffectFn2 _abortErr