├── .EditorConfig ├── .github └── workflows │ └── main.yml ├── .gitignore ├── Blazor.Streams.sln ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs └── icon.png ├── samples └── KristofferStrube.Blazor.Streams.WasmExample │ ├── App.razor │ ├── KristofferStrube.Blazor.Streams.WasmExample.csproj │ ├── Pages │ ├── CreateReadableStream.razor │ ├── CreateWritableStream.razor │ ├── Index.razor │ ├── IterateReadableStream.razor │ ├── ReadableStreamCopyTo.razor │ ├── Status.razor │ ├── StreamReadAsByteArrays.razor │ ├── StreamReadAsStrings.razor │ └── WritableStreamCopyTo.razor │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── Shared │ ├── MainLayout.razor │ ├── MainLayout.razor.css │ ├── NavMenu.razor │ └── NavMenu.razor.css │ ├── _Imports.razor │ └── wwwroot │ ├── 404.html │ ├── css │ ├── app.css │ ├── bootstrap │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ └── open-iconic │ │ ├── FONT-LICENSE │ │ ├── ICON-LICENSE │ │ ├── README.md │ │ └── font │ │ ├── css │ │ └── open-iconic-bootstrap.min.css │ │ └── fonts │ │ ├── open-iconic.eot │ │ ├── open-iconic.otf │ │ ├── open-iconic.svg │ │ ├── open-iconic.ttf │ │ └── open-iconic.woff │ ├── favicon.png │ ├── icon-192.png │ ├── images │ └── mountain.jpg │ └── index.html └── src └── KristofferStrube.Blazor.Streams ├── ArrayBufferView.cs ├── BaseJSStreamableWrapper.cs ├── BaseJSWrapper.cs ├── EnumDescriptionConverter.cs ├── Enums ├── ReadableStreamReaderMode.cs └── ReadableStreamType.cs ├── Extensions └── IJSRuntimeExtensions.cs ├── KristofferStrube.Blazor.Streams.csproj ├── Options ├── ByteLengthQueuingStrategy.InProcess.cs ├── ByteLengthQueuingStrategy.cs ├── CountQueuingStrategy.InProcess.cs ├── CountQueuingStrategy.cs ├── QueuingStrategy.cs ├── QueuingStrategyInit.cs ├── ReadableStreamBYOBReaderReadOptions.cs ├── ReadableStreamGetReaderOptions.cs ├── StreamPipeOptions.cs ├── Transformer.InProcess.cs ├── Transformer.cs ├── UnderlyingSink.InProcess.cs ├── UnderlyingSink.cs ├── UnderlyingSource.InProcess.cs └── UnderlyingSource.cs ├── ReadableStream ├── ReadableByteStreamController.InProcess.cs ├── ReadableByteStreamController.cs ├── ReadableStream.InProcess.cs ├── ReadableStream.Stream.cs ├── ReadableStream.cs ├── ReadableStreamBYOBReader.InProcess.cs ├── ReadableStreamBYOBReader.cs ├── ReadableStreamBYOBRequest.InProcess.cs ├── ReadableStreamBYOBRequest.cs ├── ReadableStreamController.cs ├── ReadableStreamDefaultController.InProcess.cs ├── ReadableStreamDefaultController.cs ├── ReadableStreamDefaultReader.InProcess.cs ├── ReadableStreamDefaultReader.cs ├── ReadableStreamReadResult.InProcess.cs ├── ReadableStreamReadResult.cs └── ReadableStreamReader.cs ├── ReadableWritablePair.InProcess.cs ├── ReadableWritablePair.cs ├── TransformStream ├── IGenericTransformStream.InProcess.cs ├── IGenericTransformStream.cs ├── TransformStream.InProcess.cs ├── TransformStream.cs ├── TransformStreamDefaultController.InProcess.cs └── TransformStreamDefaultController.cs ├── WritableStream ├── WritableStream.InProcess.cs ├── WritableStream.Stream.cs ├── WritableStream.cs ├── WritableStreamDefaultController.InProcess.cs ├── WritableStreamDefaultController.cs ├── WritableStreamDefaultWriter.InProcess.cs └── WritableStreamDefaultWriter.cs ├── _Imports.razor └── wwwroot └── KristofferStrube.Blazor.Streams.js /.EditorConfig: -------------------------------------------------------------------------------- 1 | [*] 2 | # All files 3 | dotnet_style_qualification_for_field = false 4 | dotnet_style_qualification_for_property = false 5 | dotnet_style_qualification_for_method = false 6 | dotnet_style_qualification_for_event = false 7 | dotnet_diagnostic.IDE0003.severity = warning 8 | dotnet_style_predefined_type_for_locals_parameters_members = true 9 | dotnet_style_predefined_type_for_member_access = true 10 | dotnet_diagnostic.IDE0049.severity = suggestion 11 | csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async 12 | dotnet_diagnostic.IDE0036.severity = error 13 | dotnet_style_require_accessibility_modifiers = always 14 | dotnet_diagnostic.IDE0040.severity = warning 15 | dotnet_style_readonly_field = true 16 | dotnet_diagnostic.IDE0044.severity = error 17 | csharp_prefer_static_local_function = true 18 | dotnet_diagnostic.IDE0062.severity = warning 19 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity 20 | dotnet_diagnostic.IDE0047.severity = warning 21 | dotnet_diagnostic.IDE0048.severity = warning 22 | dotnet_diagnostic.IDE0010.severity = error 23 | dotnet_style_object_initializer = true 24 | dotnet_diagnostic.IDE0017.severity = suggestion 25 | csharp_style_inlined_variable_declaration = true 26 | dotnet_diagnostic.IDE0018.severity = suggestion 27 | dotnet_style_collection_initializer = true 28 | dotnet_diagnostic.IDE0028.severity = warning 29 | dotnet_style_prefer_auto_properties = true 30 | dotnet_diagnostic.IDE0032.severity = suggestion 31 | dotnet_style_explicit_tuple_names = true 32 | dotnet_diagnostic.IDE0033.severity = warning 33 | csharp_prefer_simple_default_expression = false 34 | dotnet_diagnostic.IDE0034.severity = warning 35 | dotnet_style_prefer_inferred_tuple_names = true 36 | dotnet_style_prefer_inferred_anonymous_type_member_names = true 37 | dotnet_diagnostic.IDE0037.severity = suggestion 38 | csharp_style_prefer_local_over_anonymous_function = true 39 | dotnet_diagnostic.IDE0039.severity = warning 40 | csharp_style_deconstructed_variable_declaration = true 41 | dotnet_diagnostic.IDE0042.severity = suggestion 42 | dotnet_style_prefer_conditional_expression_over_assignment = true 43 | dotnet_diagnostic.IDE0045.severity = warning 44 | dotnet_style_prefer_conditional_expression_over_return = true 45 | dotnet_diagnostic.IDE0046.severity = warning 46 | dotnet_style_prefer_compound_assignment = true 47 | dotnet_diagnostic.IDE0054.severity = warning 48 | dotnet_diagnostic.IDE0074.severity = warning 49 | csharp_style_prefer_index_operator = true 50 | dotnet_diagnostic.IDE0056.severity = warning 51 | csharp_style_prefer_range_operator = true 52 | dotnet_diagnostic.IDE0057.severity = warning 53 | dotnet_diagnostic.IDE0070.severity = error 54 | dotnet_style_prefer_simplified_interpolation = true 55 | dotnet_diagnostic.IDE0071.severity = warning 56 | dotnet_diagnostic.IDE0072.severity = error 57 | dotnet_style_prefer_simplified_boolean_expressions = true 58 | dotnet_diagnostic.IDE0075.severity = warning 59 | dotnet_diagnostic.IDE0082.severity = error 60 | csharp_style_implicit_object_creation_when_type_is_apparent = true 61 | dotnet_diagnostic.IDE0090.severity = error 62 | dotnet_diagnostic.IDE0180.severity = warning 63 | csharp_style_namespace_declarations = file_scoped 64 | dotnet_diagnostic.IDE0160.severity = error 65 | dotnet_diagnostic.IDE0161.severity = error 66 | csharp_style_throw_expression = true 67 | dotnet_diagnostic.IDE0016.severity = warning 68 | dotnet_style_coalesce_expression = true 69 | dotnet_diagnostic.IDE0029.severity = warning 70 | dotnet_diagnostic.IDE0030.severity = warning 71 | dotnet_style_null_propagation = true 72 | dotnet_diagnostic.IDE0031.severity = warning 73 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true 74 | dotnet_diagnostic.IDE0041.severity = warning 75 | csharp_style_prefer_null_check_over_type_check = true 76 | dotnet_diagnostic.IDE0150.severity = warning 77 | csharp_style_conditional_delegate_call = false 78 | dotnet_diagnostic.IDE1005.severity = warning 79 | csharp_style_var_for_built_in_types = false 80 | csharp_style_var_when_type_is_apparent = true 81 | csharp_style_var_elsewhere = false 82 | dotnet_diagnostic.IDE0007.severity = warning 83 | dotnet_diagnostic.IDE0008.severity = warning 84 | dotnet_diagnostic.IDE0001.severity = error 85 | dotnet_diagnostic.IDE0002.severity = error 86 | dotnet_diagnostic.IDE0004.severity = error 87 | dotnet_diagnostic.IDE0005.severity = error 88 | dotnet_diagnostic.IDE0035.severity = warning 89 | dotnet_diagnostic.IDE0051.severity = warning 90 | dotnet_diagnostic.IDE0052.severity = warning 91 | csharp_style_unused_value_expression_statement_preference = discard_variable 92 | dotnet_diagnostic.IDE0058.severity = warning 93 | csharp_style_unused_value_assignment_preference = discard_variable 94 | dotnet_diagnostic.IDE0059.severity = warning -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: 'Publish application' 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths-ignore: 8 | - '**/README.md' 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | # Checkout the code 15 | - uses: actions/checkout@v2 16 | 17 | # Install .NET 8.0 SDK 18 | - name: Setup .NET 8 preview 19 | uses: actions/setup-dotnet@v3 20 | with: 21 | dotnet-version: '8.0.x' 22 | include-prerelease: true 23 | 24 | # Generate the website 25 | - name: Publish 26 | run: dotnet publish samples/KristofferStrube.Blazor.Streams.WasmExample/KristofferStrube.Blazor.Streams.WasmExample.csproj --configuration Release --output build 27 | 28 | # Publish the website 29 | - name: GitHub Pages action 30 | if: ${{ github.ref == 'refs/heads/main' }} # Publish only when the push is on main 31 | uses: peaceiris/actions-gh-pages@v3.6.1 32 | with: 33 | github_token: ${{ secrets.PUBLISH_TOKEN }} 34 | publish_branch: gh-pages 35 | publish_dir: build/wwwroot 36 | allow_empty_commit: false 37 | keep_files: false 38 | force_orphan: true 39 | -------------------------------------------------------------------------------- /Blazor.Streams.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KristofferStrube.Blazor.Streams", "src\KristofferStrube.Blazor.Streams\KristofferStrube.Blazor.Streams.csproj", "{C055529E-49A5-4BA6-9049-9D0FF4CBA7BF}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KristofferStrube.Blazor.Streams.WasmExample", "samples\KristofferStrube.Blazor.Streams.WasmExample\KristofferStrube.Blazor.Streams.WasmExample.csproj", "{43126A7F-10BE-41C9-BB23-EAA52F96CA05}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(SolutionProperties) = preSolution 16 | HideSolutionNode = FALSE 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {C055529E-49A5-4BA6-9049-9D0FF4CBA7BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {C055529E-49A5-4BA6-9049-9D0FF4CBA7BF}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {C055529E-49A5-4BA6-9049-9D0FF4CBA7BF}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {C055529E-49A5-4BA6-9049-9D0FF4CBA7BF}.Release|Any CPU.Build.0 = Release|Any CPU 23 | {43126A7F-10BE-41C9-BB23-EAA52F96CA05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {43126A7F-10BE-41C9-BB23-EAA52F96CA05}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {43126A7F-10BE-41C9-BB23-EAA52F96CA05}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {43126A7F-10BE-41C9-BB23-EAA52F96CA05}.Release|Any CPU.Build.0 = Release|Any CPU 27 | EndGlobalSection 28 | GlobalSection(NestedProjects) = preSolution 29 | {C055529E-49A5-4BA6-9049-9D0FF4CBA7BF} = {09D72B7A-1CB1-49BA-9312-2358E79A7DCE} 30 | {43126A7F-10BE-41C9-BB23-EAA52F96CA05} = {BC97D913-7EFD-4215-AB60-0039CB00703D} 31 | EndGlobalSection 32 | EndGlobal 33 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [0.5.0] - 2024-10-23 10 | ### Changed 11 | - Changed the version of `Blazor.DOM` to use the newest version which is `0.3.0`. 12 | 13 | ## [0.4.2] - 2024-03-11 14 | ### Changed 15 | - Changed `ReadableStreamReadResultInProcess.Value` to return `IJSInProcessObjectReference` instead of `IJSObjectReference`. 16 | 17 | ## [0.4.1] - 2024-03-10 18 | ### Fixed 19 | - Fixed that `ReadableStreamBYOBReaderInProcess` did not invoke read on the `JSReference` itself. 20 | 21 | ## [0.4.0] - 2024-03-10 22 | ### Deprecated 23 | - Deprecated `ArrayBufferView` as the `IArrayBufferView` interface from `Blazor.WebIDL` should be used instead. 24 | ### Changed 25 | - Changed the version of `Blazor.WebIDL` to use the newest version which is `0.5.0`. 26 | ### Added 27 | - Added `Signal` property to `StreamPipeOptions` using the `AbortSignal` type from `Blazor.DOM`. 28 | - Added `ReadableStreamBYOBReaderReadOptions` parameter to `ReadableStreamBYOBReader.ReadAsync` method and in-process version. 29 | 30 | ## [0.3.0] - 2023-03-15 31 | ### Changed 32 | - Changed .NET version to `7.0`. 33 | - Marked all `Create` methods as `Obsolete` and suggests to use `CreateAsync` instead. 34 | ### Added 35 | - Added `IGenericTransformStream` and `IGenericTransformStreamInProcess` interfaces for use with the `PipeThroughAsync` method. 36 | - Added the generation of a documentation file packaging all XML comments with the package. 37 | - Added `CreateAsync` methods to all wrapper instances as an alternative to the previous synchronous `Create` methods. 38 | 39 | ## [0.2.2] - 2022-11-09 40 | ### Fixed 41 | - Fixed that `WritableStream`' did not call `CloseAsync` when invoking `DisposeAsync`. 42 | 43 | ## [0.2.1] - 2022-11-09 44 | ### Fixed 45 | - Fixed that `WritableStream`'s constructor was internal and made it protected instead. 46 | 47 | ## [0.2.0] - 2022-10-31 48 | ### Added 49 | - `ReadableStream` now extends `Stream` and can be read from by other .NET streams using `CopyToAsync`. 50 | - `WritableStream` now extends `Stream` and can be written to by other .NET streams using `CopyToAsync`. 51 | 52 | ## [0.1.0] - 2022-10-30 53 | ### Added 54 | - Made the initial implementation that covers most of the API. -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | Contributing includes many different actions. It is not only writing code. Contributions like discussing solutions in open issues are easily as valuable as contributing code as we can then find the best solution with the perspective of multiple people. 3 | 4 | ## Bugs and feature requests 5 | If you find any bugs in the project or have requests for features, then please [Open a new Issue](https://github.com/KristofferStrube/Blazor.Streams/issues/new). 6 | 7 | ## Contributing Code and Samples 8 | Before you contribute to the project, you will need to get confirmation from the library author that the contribution is welcome. 9 | This can help align the scope of the contribution so that you and the author agree on the solution and how you ensure the change is maintainable with the existing users in mind. 10 | Once you are ready to start coding try to follow these code guidelines: 11 | - Make a fork of the current `main` branch. 12 | - Follow the existing coding conventions used in the project. This includes tab style, naming conventions, etc. 13 | - If your contribution is a new feature, try to add a demo that demonstrates how this will be used in the sample project. 14 | - Any code or sample you share as a part of the resulting Pull Request should fall under the MIT license agreement. 15 | - You don't need to update the version number of the project as the maintainer will do this when making the next release after the Pull Request has been merged. 16 | - Keep your Pull Request to the point. I.e., if your Pull Request is related to fixing a bug then try not to touch any other files than the ones related to that issue as this will make the chance of the PR being merged without change requests more likely. 17 | 18 | ## Submitting a Pull Request 19 | If you don't know what a pull request is, read this article: https://help.github.com/articles/using-pull-requests. Make sure the repository can be built and that the related sample project still works as intended. It is also a good idea to familiarize yourself with the project workflow and our coding conventions. 20 | 21 | ## Ensuring that your contribution will be accepted 22 | You might also read these two blog posts on contributing code: [Open Source Contribution Etiquette](http://tirania.org/blog/archive/2010/Dec-31.html) by Miguel de Icaza and [Don't "Push" Your Pull Requests](https://www.igvita.com/2011/12/19/dont-push-your-pull-requests/) by Ilya Grigorik. These blog posts highlight good open-source collaboration etiquette and help align expectations between you and us. 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Kristoffer Strube 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](/LICENSE.md) 2 | [![GitHub issues](https://img.shields.io/github/issues/KristofferStrube/Blazor.Streams)](https://github.com/KristofferStrube/Blazor.Streams/issues) 3 | [![GitHub forks](https://img.shields.io/github/forks/KristofferStrube/Blazor.Streams)](https://github.com/KristofferStrube/Blazor.Streams/network/members) 4 | [![GitHub stars](https://img.shields.io/github/stars/KristofferStrube/Blazor.Streams)](https://github.com/KristofferStrube/Blazor.Streams/stargazers) 5 | 6 | [![NuGet Downloads (official NuGet)](https://img.shields.io/nuget/dt/KristofferStrube.Blazor.Streams?label=NuGet%20Downloads)](https://www.nuget.org/packages/KristofferStrube.Blazor.Streams/) 7 | 8 | # Introduction 9 | A Blazor wrapper for the browser API [Streams](https://streams.spec.whatwg.org/) 10 | 11 | The API standardizes ways to create, compose, and consume streams of data that map to low-level I/O primitives in the browser. This project implements a wrapper around the API for Blazor so that we can easily and safely interact with the streams of the browser. 12 | 13 | # Demo 14 | The sample project can be demoed at https://kristofferstrube.github.io/Blazor.Streams/ 15 | 16 | On each page you can find the corresponding code for the example in the top right corner. 17 | 18 | On the *API Coverage Status* page you can get an overview over what parts of the API we support currently. 19 | 20 | # Getting Started 21 | The package can be used in Blazor projects. 22 | ## Prerequisites 23 | You need to install .NET 7.0 or newer to use the library. 24 | 25 | [Download .NET 7](https://dotnet.microsoft.com/download/dotnet/7.0) 26 | 27 | ## Installation 28 | You can install the package via Nuget with the Package Manager in your IDE or alternatively using the command line: 29 | ```bash 30 | dotnet add package KristofferStrube.Blazor.Streams 31 | ``` 32 | 33 | ## Import 34 | You need to reference the package in order to use it in your pages. This can be done in `_Import.razor` by adding the following. 35 | ```razor 36 | @using KristofferStrube.Blazor.Streams 37 | ``` 38 | ## Creating wrapper instance 39 | We can call the constructor for `ReadableStream`, `WritableStream`, or `TransformStream` from C# and work on these objects like so: 40 | ```razor 41 | @inject IJSInProcessRuntime JSRuntime 42 | 43 | @code { 44 | protected override async Task OnInitializedAsync() 45 | { 46 | // Construct a stream in .NET. 47 | using var data = new System.IO.MemoryStream(new byte[1000 * 1024]); 48 | 49 | // Convert a .NET Stream to a JS ReadableStream. 50 | using var streamRef = new DotNetStreamReference(stream: data, leaveOpen: false); 51 | var jSStreamReference = await JSRuntime.InvokeAsync("jSStreamReference", streamRef); 52 | 53 | // Create a wrapper instance of the ReadableStream. 54 | var readableStream = await ReadableStream.CreateAsync(JSRuntime, jSStreamReference); 55 | 56 | // Get the reader and iterate that. 57 | var readableStreamReader = await readableStream.GetDefaultReaderAsync(); 58 | await foreach (var chunk in readableStreamReader) 59 | { 60 | var length = await JSRuntime.InvokeAsync("getAttribute", chunk, "length"); 61 | Console.WriteLine(length); 62 | await Task.Delay(100); 63 | } 64 | } 65 | } 66 | ``` 67 | 68 | For the above example we use two small JavaScript functions that basically serves as a way to convert the .NET `DotNetStreamReference` object into an `IJSObjectReference` and a way to get any attribute of an `IJSObjectReference`. These methods looks like this and can be defined in the `index.html` or `_host.razor` page. 69 | ```javascript 70 | function jSStreamReference(streamRef) { return streamRef.stream(); } 71 | function getAttribute(object, attribute) { return object[attribute]; } 72 | ``` 73 | 74 | # Issues 75 | Feel free to open issues on the repository if you find any errors with the package or have wishes for features. 76 | 77 | # Related articles 78 | This repository was build with inspiration and help from the following series of articles: 79 | 80 | - [Wrapping JavaScript libraries in Blazor WebAssembly/WASM](https://blog.elmah.io/wrapping-javascript-libraries-in-blazor-webassembly-wasm/) 81 | - [Call anonymous C# functions from JS in Blazor WASM](https://blog.elmah.io/call-anonymous-c-functions-from-js-in-blazor-wasm/) 82 | - [Using JS Object References in Blazor WASM to wrap JS libraries](https://blog.elmah.io/using-js-object-references-in-blazor-wasm-to-wrap-js-libraries/) 83 | - [Blazor WASM 404 error and fix for GitHub Pages](https://blog.elmah.io/blazor-wasm-404-error-and-fix-for-github-pages/) 84 | - [How to fix Blazor WASM base path problems](https://blog.elmah.io/how-to-fix-blazor-wasm-base-path-problems/) 85 | -------------------------------------------------------------------------------- /docs/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KristofferStrube/Blazor.Streams/7c3b01eb79ce7a5d397f32cbbbf40fe29dd4f30c/docs/icon.png -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/KristofferStrube.Blazor.Streams.WasmExample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/Pages/CreateReadableStream.razor: -------------------------------------------------------------------------------- 1 | @page "/CreateReadableStream" 2 | @inject IJSInProcessRuntime JSRuntime 3 | 4 | Streams - Create ReadableStream 5 | 6 |

Creating a ReadableStream

7 | We can create a new instance of a ReadableStream that can generate a byte array which when the ReadableStream is started inserts 20 1´s and after that generates 0´s equvalent to the desired amount but where it takes 100 miliseconds to generate each. 8 | The for-loop that consumes the ReadableStream also limits its consumption by only requesitng a chunk every 100 milisecond. 9 | 10 | @for (int i = 0; i < chunks.Count; i++) 11 | { 12 |
chunk @i: @chunks[i]
13 | } 14 | 15 | @code { 16 | private List chunks = new(); 17 | 18 | protected override async Task OnInitializedAsync() 19 | { 20 | await using ReadableStream readableStream = await ReadableStream.CreateAsync(JSRuntime, new UnderlyingSource(JSRuntime) 21 | { 22 | Start = async (ctr) => 23 | { 24 | var controller = ((ReadableStreamDefaultController)ctr); 25 | var byteArray = await JSRuntime.InvokeAsync("byteArray", Enumerable.Range(0, 20).Select(_ => (byte)1).ToArray()); 26 | await controller.EnqueueAsync(byteArray); 27 | }, 28 | Pull = async (ctr) => 29 | { 30 | var controller = ((ReadableStreamDefaultController)ctr); 31 | await Task.Delay(100); 32 | var size = await controller.GetDesiredSizeAsync(); 33 | if (size is > 0) 34 | { 35 | var byteArray = await JSRuntime.InvokeAsync("byteArray", new byte[(int)size]); 36 | await controller.EnqueueAsync(byteArray); 37 | } 38 | } 39 | }, new QueuingStrategy() 40 | { 41 | HighWaterMark = 50, 42 | Size = _ => 10, 43 | }); 44 | await using ReadableStreamDefaultReader reader = await readableStream.GetDefaultReaderAsync(); 45 | await foreach (var chunk in reader.IterateByteArraysAsync()) 46 | { 47 | if (chunk is not null) 48 | { 49 | await Task.Delay(100); 50 | chunks.Add(string.Join("", chunk.Select(b => b.ToString()))); 51 | StateHasChanged(); 52 | } 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/Pages/CreateWritableStream.razor: -------------------------------------------------------------------------------- 1 | @page "/CreateWritableStream" 2 | @inject IJSInProcessRuntime JSRuntime 3 | 4 | Streams - Create WritableStream 5 | 6 |

Creating a WritableStream

7 | We can create a new instance of a WritableStream which can receive writes and send them somewhere. In our case our WritableStream will write to a list that we display. 8 | 9 | @for (int i = 0; i < chunkSizes.Count; i++) 10 | { 11 |
chunk @i size: @chunkSizes[i]
12 | } 13 | 14 | @code { 15 | private List chunkSizes = new(); 16 | 17 | protected override async Task OnInitializedAsync() 18 | { 19 | WritableStream writableStream = await WritableStream.CreateAsync(JSRuntime, new UnderlyingSink(JSRuntime) 20 | { 21 | Write = async (chunk, controller) => 22 | { 23 | var length = await JSRuntime.InvokeAsync("getAttribute", chunk, "length"); 24 | chunkSizes.Add(length); 25 | StateHasChanged(); 26 | } 27 | }, await ByteLengthQueuingStrategy.CreateAsync(JSRuntime, new QueuingStrategyInit(20))); 28 | 29 | await using WritableStreamDefaultWriter writer = await writableStream.GetWriterAsync(); 30 | for (int i = 0; i < 10; i++) 31 | { 32 | await Task.Delay(100); 33 | var size = await writer.GetDesiredSizeAsync(); 34 | var byteArray = await JSRuntime.InvokeAsync("byteArray", new byte[(int)size]); 35 | await writer.WriteAsync(byteArray); 36 | } 37 | await writer.CloseAsync(); 38 | } 39 | } -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @using KristofferStrube.Blazor.WebIDL 3 | @inject IJSInProcessRuntime JSRuntime 4 | 5 | Streams - Index 6 | 7 |

Reading a Stream

8 | On this page we create a MemoryStream of @dataSize bytes in C# and parse that to JavaScript to create a IJSObjectReference for the stream. 9 |
10 | From the JS reference we create a ReadableStreamInProcess and gets the ReadableStreamDefaultReader of that. 11 |
12 | Using the reader we read the chunks until there are no more. 13 | @for (int i = 0; i < chunkSizes.Count; i++) 14 | { 15 |
chunk @i size: @chunkSizes[i] bytes
16 | } 17 | 18 | @code { 19 | private List chunkSizes = new(); 20 | private int dataSize = 1000 * 1024; 21 | 22 | protected override async Task OnInitializedAsync() 23 | { 24 | using Stream data = new System.IO.MemoryStream(new byte[dataSize]); 25 | using DotNetStreamReference streamRef = new DotNetStreamReference(stream: data, leaveOpen: false); 26 | await using IJSInProcessObjectReference jSStreamReference = await JSRuntime.InvokeAsync("jSStreamReference", streamRef); 27 | await using ReadableStreamInProcess readableStream = await ReadableStreamInProcess.CreateAsync(JSRuntime, jSStreamReference); 28 | await using ReadableStreamDefaultReaderInProcess readableStreamReader = readableStream.GetDefaultReader(); 29 | ReadableStreamReadResultInProcess read = await readableStreamReader.ReadAsync(); 30 | while (!read.Done) 31 | { 32 | await using Uint8ArrayInProcess chunk = await Uint8ArrayInProcess.CreateAsync( 33 | JSRuntime, 34 | read.Value, 35 | new() { DisposesJSReference = true } 36 | ); 37 | var length = chunk.Length; 38 | chunkSizes.Add(length); 39 | await Task.Delay(100); 40 | await read.DisposeAsync(); 41 | read = await readableStreamReader.ReadAsync(); 42 | StateHasChanged(); 43 | } 44 | await read.DisposeAsync(); 45 | } 46 | } -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/Pages/IterateReadableStream.razor: -------------------------------------------------------------------------------- 1 | @page "/IterateReadableStream" 2 | @inject IJSInProcessRuntime JSRuntime 3 | 4 | Streams - Iterate ReadableStream 5 | 6 |

Iterating a ReadableStream

7 | On this page we create a MemoryStream of @dataSize bytes in C# and parse that to JavaScript to create a IJSObjectReference for the stream. 8 |
9 | From the JS reference we create a ReadableStream and gets the ReadableStreamDefaultReader of that. 10 |
11 | We iterate the ReadableStream using await foreach(var chunk in reader) 12 | @for (int i = 0; i < chunkSizes.Count; i++) 13 | { 14 |
chunk @i size: @chunkSizes[i] bytes
15 | } 16 | 17 | 18 | @code { 19 | private List chunkSizes = new(); 20 | private int dataSize = 1000 * 1024; 21 | 22 | protected override async Task OnInitializedAsync() 23 | { 24 | using Stream data = new System.IO.MemoryStream(new byte[dataSize]); 25 | using DotNetStreamReference streamRef = new DotNetStreamReference(stream: data, leaveOpen: false); 26 | await using IJSInProcessObjectReference jSStreamReference = await JSRuntime.InvokeAsync("jSStreamReference", streamRef); 27 | await using ReadableStream readableStream = await ReadableStream.CreateAsync(JSRuntime, jSStreamReference); 28 | await using ReadableStreamDefaultReader reader = await readableStream.GetDefaultReaderAsync(); 29 | await foreach (var chunk in reader) 30 | { 31 | var length = await JSRuntime.InvokeAsync("getAttribute", chunk, "length"); 32 | chunkSizes.Add(length); 33 | await Task.Delay(100); 34 | StateHasChanged(); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/Pages/ReadableStreamCopyTo.razor: -------------------------------------------------------------------------------- 1 | @page "/ReadableStreamCopyTo" 2 | @inject IJSInProcessRuntime JSRuntime 3 | @inject HttpClient HttpClient 4 | 5 | Streams - ReadableStream CopyTo 6 | 7 |

Stream copying a ReadableStream

8 | ReadableStream inherits from the .NET Stream class. This means that we can use the CopyToAsync method to copy the content of it to any other .NET Stream. 9 |
10 | In this sample we copy a ReadableStream containing an image to a .NET MemoryStream and converts that to its base64 encoding to display it in a img-tag. 11 |
12 | 13 | 14 | @code { 15 | string imageUrl = ""; 16 | 17 | protected override async Task OnInitializedAsync() 18 | { 19 | using Stream data = await HttpClient.GetStreamAsync("images/mountain.jpg"); 20 | using DotNetStreamReference streamRef = new DotNetStreamReference(stream: data, leaveOpen: false); 21 | await using IJSInProcessObjectReference jSStreamReference = await JSRuntime.InvokeAsync("jSStreamReference", streamRef); 22 | await using ReadableStreamInProcess readableStream = await ReadableStreamInProcess.CreateAsync(JSRuntime, jSStreamReference); 23 | 24 | using MemoryStream writeStream = new System.IO.MemoryStream(); 25 | await readableStream.CopyToAsync(writeStream); 26 | imageUrl = "data:image/png;base64," + Convert.ToBase64String(writeStream.ToArray()); 27 | } 28 | } -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/Pages/StreamReadAsByteArrays.razor: -------------------------------------------------------------------------------- 1 | @page "/StreamReadAsByteArrays" 2 | @inject IJSInProcessRuntime JSRuntime 3 | 4 | Streams - Iterate As ByteArrays 5 | 6 |

Reading a stream as byte arrays

7 | On this page we create a MemoryStream of @dataSize bytes in C# and parse that to JavaScript to create a IJSObjectReference for the stream. 8 |
9 | From the JS reference we create a ReadableStream and gets it default reader. 10 |
11 | We iterate the reader using await foreach (byte[] chunk in reader.IterateByteArrays()) 12 | @for (int i = 0; i < chunkSizes.Count; i++) 13 | { 14 |
chunk @i size: @chunkSizes[i] bytes
15 | } 16 | 17 | 18 | @code { 19 | private List chunkSizes = new(); 20 | private int dataSize = 1000 * 1024; 21 | 22 | protected override async Task OnInitializedAsync() 23 | { 24 | using Stream data = new System.IO.MemoryStream(new byte[dataSize]); 25 | using DotNetStreamReference streamRef = new DotNetStreamReference(stream: data, leaveOpen: false); 26 | IJSInProcessObjectReference jSStreamReference = await JSRuntime.InvokeAsync("jSStreamReference", streamRef); 27 | ReadableStream readableStream = await ReadableStream.CreateAsync(JSRuntime, jSStreamReference); 28 | ReadableStreamDefaultReader reader = await readableStream.GetDefaultReaderAsync(); 29 | await foreach (byte[] chunk in reader.IterateByteArraysAsync()) 30 | { 31 | chunkSizes.Add(chunk.Length); 32 | await Task.Delay(100); 33 | StateHasChanged(); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/Pages/StreamReadAsStrings.razor: -------------------------------------------------------------------------------- 1 | @page "/StreamReadAsStrings" 2 | @inject IJSInProcessRuntime JSRuntime 3 | 4 | Streams - Iterate As ByteArrays 5 | 6 |

Reading a stream as byte arrays

7 | On this page we create a MemoryStream of @dataSize bytes in C# from a Lorem Ipsum string and parse that to JavaScript to create a IJSObjectReference for the stream. 8 |
9 | From the JS reference we create a ReadableStream and gets it default reader. 10 |
11 | We iterate the reader using await foreach (string chunk in reader.IterateStrings()) and appends each chunk to the shown result. 12 |
13 | @result 14 | 15 | 16 | @code { 17 | private System.Text.Encoding encoding = System.Text.Encoding.Unicode; 18 | private string result = ""; 19 | private int dataSize = 0; 20 | private string loremIpsum = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis id nulla orci. Donec id tellus sagittis, suscipit turpis ac, efficitur magna. Phasellus sit amet rutrum nisl, ut tristique enim. Integer nec purus nec enim blandit laoreet. Cras maximus luctus interdum. Phasellus pellentesque tristique neque imperdiet tincidunt. Pellentesque bibendum tristique ipsum at fringilla. In ornare in dolor sit amet pellentesque. Maecenas fermentum mi purus, vitae malesuada lorem gravida quis. In auctor accumsan mattis. 21 | 22 | Nunc sed elit sem.Curabitur pharetra nunc sed diam venenatis, vel lacinia nulla volutpat. Praesent ornare congue nisi, ut porttitor leo dignissim id.Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam nulla orci, commodo sed massa in, elementum tristique massa.Donec varius augue dui, consectetur molestie nisi feugiat eu.Morbi id leo non ligula ullamcorper dignissim. Duis sit amet posuere ante.Pellentesque hendrerit pretium orci, eu tincidunt purus semper pellentesque.Maecenas dapibus quam ac ex commodo, at dignissim arcu consequat. 23 | 24 | Donec congue nisi eget turpis porta, a posuere nisl blandit. Suspendisse potenti. Sed sed est aliquet, faucibus sapien in, tincidunt mi. Sed vulputate gravida lacus sit amet laoreet.Donec egestas dolor sed risus hendrerit volutpat.Pellentesque tortor nisi, ultrices non nibh quis, tristique molestie leo.In rutrum purus vel iaculis tincidunt. 25 | 26 | Sed lorem lectus, vestibulum ut orci ut, vulputate interdum nibh.Duis vehicula turpis neque, efficitur lacinia quam suscipit sit amet. Morbi sit amet tempor lacus.Quisque pretium gravida ornare. Nullam aliquet tincidunt gravida. Suspendisse nec pellentesque sapien. Proin eget elit mattis, hendrerit velit at, pulvinar risus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.Quisque feugiat odio ut ullamcorper consequat. Proin sit amet felis malesuada, suscipit quam id, ullamcorper tellus. Vivamus scelerisque aliquam aliquet. Nunc nec commodo tellus, ut accumsan urna.Aliquam et odio ac odio tincidunt dictum. 27 | 28 | Cras efficitur libero sed justo ultricies congue et in lacus.In elit orci, pharetra sit amet magna sit amet, faucibus auctor neque.Cras molestie dignissim dui ac porta. Quisque dictum lorem feugiat ipsum ultricies, sit amet malesuada enim blandit.Vivamus luctus, elit ut ultrices iaculis, nisl augue convallis dui, ac euismod leo leo non arcu. Ut at justo velit. Quisque pulvinar enim non malesuada ultrices. Etiam non lacus lacus. 😁"; 29 | 30 | protected override void OnInitialized() 31 | { 32 | dataSize = encoding.GetBytes(loremIpsum).Length; 33 | } 34 | 35 | protected override async Task OnInitializedAsync() 36 | { 37 | using Stream data = new System.IO.MemoryStream(); 38 | using StreamWriter writer = new StreamWriter(data, encoding); 39 | writer.Write(loremIpsum); 40 | writer.Flush(); 41 | data.Position = 0; 42 | using DotNetStreamReference streamRef = new DotNetStreamReference(stream: data, leaveOpen: false); 43 | await using IJSInProcessObjectReference jSStreamReference = await JSRuntime.InvokeAsync("jSStreamReference", streamRef); 44 | await using ReadableStream readableStream = await ReadableStream.CreateAsync(JSRuntime, jSStreamReference); 45 | await using ReadableStreamDefaultReader reader = await readableStream.GetDefaultReaderAsync(); 46 | await foreach (string chunk in reader.IterateStringsAsync(encoding)) 47 | { 48 | result += chunk; 49 | await Task.Delay(100); 50 | StateHasChanged(); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/Pages/WritableStreamCopyTo.razor: -------------------------------------------------------------------------------- 1 | @page "/WritableStreamCopyTo" 2 | @inject IJSInProcessRuntime JSRuntime 3 | @inject HttpClient HttpClient 4 | 5 | Streams - WritableStream CopyTo 6 | 7 |

Stream copying to a WritableStream

8 | WritableStream inherits from the .NET Stream class. This means that we can use the CopyToAsync method to copy the content of any other .NET Stream to it. 9 |
10 | In this sample we copy a .NET Stream containing an image to a WritableStream that streams its content to a base64 encoding which a image-tag uses as its source. 11 |
12 | 13 | 14 | @code { 15 | string imageUrl = ""; 16 | 17 | protected override async Task OnInitializedAsync() 18 | { 19 | using Stream data = await HttpClient.GetStreamAsync("images/mountain.jpg"); 20 | 21 | WritableStream writableStream = await WritableStream.CreateAsync(JSRuntime, new UnderlyingSink(JSRuntime) 22 | { 23 | Start = (_) => 24 | { 25 | imageUrl = "data:image/png;base64,"; 26 | return Task.CompletedTask; 27 | }, 28 | Write = async (chunk, _) => 29 | { 30 | var bytes = await chunk.InvokeAsync("valueOf"); 31 | imageUrl += Convert.ToBase64String(bytes); 32 | StateHasChanged(); 33 | } 34 | }, new QueuingStrategy()); 35 | 36 | await data.CopyToAsync(writableStream); 37 | } 38 | } -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/Program.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.Streams.WasmExample; 2 | using Microsoft.AspNetCore.Components.Web; 3 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 4 | using Microsoft.JSInterop; 5 | 6 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 7 | builder.RootComponents.Add("#app"); 8 | builder.RootComponents.Add("head::after"); 9 | 10 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 11 | 12 | builder.Services.AddScoped(sp => (IJSInProcessRuntime)sp.GetRequiredService()); 13 | 14 | await builder.Build().RunAsync(); 15 | -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:62524", 7 | "sslPort": 44303 8 | } 9 | }, 10 | "profiles": { 11 | "http": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": true, 15 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 16 | "applicationUrl": "http://localhost:5029", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "https": { 22 | "commandName": "Project", 23 | "dotnetRunMessages": true, 24 | "launchBrowser": true, 25 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 26 | "applicationUrl": "https://localhost:7063;http://localhost:5029", 27 | "environmentVariables": { 28 | "ASPNETCORE_ENVIRONMENT": "Development" 29 | } 30 | }, 31 | "IIS Express": { 32 | "commandName": "IISExpress", 33 | "launchBrowser": true, 34 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 35 | "environmentVariables": { 36 | "ASPNETCORE_ENVIRONMENT": "Development" 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | @inject NavigationManager NavigationManager 3 | 4 |
5 | 8 | 9 |
10 |
11 | 12 | 13 | 14 |
15 | 16 |
17 | @Body 18 |
19 |
20 |
21 | 22 | @code { 23 | private string relativeUri => NavigationManager.ToBaseRelativePath(NavigationManager.Uri); 24 | 25 | protected string page => (string.IsNullOrEmpty(relativeUri) ? "Index" : relativeUri) + ".razor"; 26 | } -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/Shared/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row ::deep .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | text-decoration: none; 28 | } 29 | 30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { 31 | text-decoration: underline; 32 | } 33 | 34 | .top-row ::deep a:first-child { 35 | overflow: hidden; 36 | text-overflow: ellipsis; 37 | } 38 | 39 | @media (max-width: 640.98px) { 40 | .top-row:not(.auth) { 41 | display: none; 42 | } 43 | 44 | .top-row.auth { 45 | justify-content: space-between; 46 | } 47 | 48 | .top-row ::deep a, .top-row ::deep .btn-link { 49 | margin-left: 0; 50 | } 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .page { 55 | flex-direction: row; 56 | } 57 | 58 | .sidebar { 59 | width: 300px; 60 | height: 100vh; 61 | position: sticky; 62 | top: 0; 63 | } 64 | 65 | .top-row { 66 | position: sticky; 67 | top: 0; 68 | z-index: 1; 69 | } 70 | 71 | .top-row.auth ::deep a:first-child { 72 | flex: 1; 73 | text-align: right; 74 | width: 0; 75 | } 76 | 77 | .top-row, article { 78 | padding-left: 2rem !important; 79 | padding-right: 1.5rem !important; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  9 | 10 | 59 | 60 | @code { 61 | private bool collapseNavMenu = true; 62 | 63 | private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null; 64 | 65 | private void ToggleNavMenu() 66 | { 67 | collapseNavMenu = !collapseNavMenu; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/Shared/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | background-color: rgba(255, 255, 255, 0.1); 3 | } 4 | 5 | .top-row { 6 | height: 3.5rem; 7 | background-color: rgba(0,0,0,0.4); 8 | } 9 | 10 | .navbar-brand { 11 | font-size: 1.1rem; 12 | } 13 | 14 | .oi { 15 | width: 2rem; 16 | font-size: 1.1rem; 17 | vertical-align: text-top; 18 | top: -2px; 19 | } 20 | 21 | .nav-item { 22 | font-size: 0.9rem; 23 | padding-bottom: 0.5rem; 24 | } 25 | 26 | .nav-item:first-of-type { 27 | padding-top: 1rem; 28 | } 29 | 30 | .nav-item:last-of-type { 31 | padding-bottom: 1rem; 32 | } 33 | 34 | .nav-item ::deep a { 35 | color: #d7d7d7; 36 | border-radius: 4px; 37 | height: 3rem; 38 | display: flex; 39 | align-items: center; 40 | line-height: 3rem; 41 | } 42 | 43 | .nav-item ::deep a.active { 44 | background-color: rgba(255,255,255,0.25); 45 | color: white; 46 | } 47 | 48 | .nav-item ::deep a:hover { 49 | background-color: rgba(255,255,255,0.1); 50 | color: white; 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .navbar-toggler { 55 | display: none; 56 | } 57 | 58 | .collapse { 59 | /* Never collapse the sidebar for wide screens */ 60 | display: block; 61 | } 62 | 63 | .nav-scrollable { 64 | /* Allow sidebar to scroll for tall menus */ 65 | height: calc(100vh - 3.5rem); 66 | overflow-y: auto; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using KristofferStrube.Blazor.Streams.WasmExample 10 | @using KristofferStrube.Blazor.Streams.WasmExample.Shared 11 | -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/404.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Blazor Streams 6 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/css/app.css: -------------------------------------------------------------------------------- 1 | @import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); 2 | 3 | html, body { 4 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 5 | } 6 | 7 | h1:focus { 8 | outline: none; 9 | } 10 | 11 | a, .btn-link { 12 | color: #0071c1; 13 | } 14 | 15 | .btn-primary { 16 | color: #fff; 17 | background-color: #1b6ec2; 18 | border-color: #1861ac; 19 | } 20 | 21 | .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { 22 | box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; 23 | } 24 | 25 | .content { 26 | padding-top: 1.1rem; 27 | } 28 | 29 | .valid.modified:not([type=checkbox]) { 30 | outline: 1px solid #26b050; 31 | } 32 | 33 | .invalid { 34 | outline: 1px solid red; 35 | } 36 | 37 | .validation-message { 38 | color: red; 39 | } 40 | 41 | #blazor-error-ui { 42 | background: lightyellow; 43 | bottom: 0; 44 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 45 | display: none; 46 | left: 0; 47 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 48 | position: fixed; 49 | width: 100%; 50 | z-index: 1000; 51 | } 52 | 53 | #blazor-error-ui .dismiss { 54 | cursor: pointer; 55 | position: absolute; 56 | right: 0.75rem; 57 | top: 0.5rem; 58 | } 59 | 60 | .blazor-error-boundary { 61 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 62 | padding: 1rem 1rem 1rem 3.7rem; 63 | color: white; 64 | } 65 | 66 | .blazor-error-boundary::after { 67 | content: "An error has occurred." 68 | } 69 | 70 | .loading-progress { 71 | position: relative; 72 | display: block; 73 | width: 8rem; 74 | height: 8rem; 75 | margin: 20vh auto 1rem auto; 76 | } 77 | 78 | .loading-progress circle { 79 | fill: none; 80 | stroke: #e0e0e0; 81 | stroke-width: 0.6rem; 82 | transform-origin: 50% 50%; 83 | transform: rotate(-90deg); 84 | } 85 | 86 | .loading-progress circle:last-child { 87 | stroke: #1b6ec2; 88 | stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%; 89 | transition: stroke-dasharray 0.05s ease-in-out; 90 | } 91 | 92 | .loading-progress-text { 93 | position: absolute; 94 | text-align: center; 95 | font-weight: bold; 96 | inset: calc(20vh + 3.25rem) 0 auto 0.2rem; 97 | } 98 | 99 | .loading-progress-text:after { 100 | content: var(--blazor-load-percentage-text, "Loading"); 101 | } 102 | -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/css/open-iconic/FONT-LICENSE: -------------------------------------------------------------------------------- 1 | SIL OPEN FONT LICENSE Version 1.1 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | PREAMBLE 6 | The goals of the Open Font License (OFL) are to stimulate worldwide 7 | development of collaborative font projects, to support the font creation 8 | efforts of academic and linguistic communities, and to provide a free and 9 | open framework in which fonts may be shared and improved in partnership 10 | with others. 11 | 12 | The OFL allows the licensed fonts to be used, studied, modified and 13 | redistributed freely as long as they are not sold by themselves. The 14 | fonts, including any derivative works, can be bundled, embedded, 15 | redistributed and/or sold with any software provided that any reserved 16 | names are not used by derivative works. The fonts and derivatives, 17 | however, cannot be released under any other type of license. The 18 | requirement for fonts to remain under this license does not apply 19 | to any document created using the fonts or their derivatives. 20 | 21 | DEFINITIONS 22 | "Font Software" refers to the set of files released by the Copyright 23 | Holder(s) under this license and clearly marked as such. This may 24 | include source files, build scripts and documentation. 25 | 26 | "Reserved Font Name" refers to any names specified as such after the 27 | copyright statement(s). 28 | 29 | "Original Version" refers to the collection of Font Software components as 30 | distributed by the Copyright Holder(s). 31 | 32 | "Modified Version" refers to any derivative made by adding to, deleting, 33 | or substituting -- in part or in whole -- any of the components of the 34 | Original Version, by changing formats or by porting the Font Software to a 35 | new environment. 36 | 37 | "Author" refers to any designer, engineer, programmer, technical 38 | writer or other person who contributed to the Font Software. 39 | 40 | PERMISSION & CONDITIONS 41 | Permission is hereby granted, free of charge, to any person obtaining 42 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 43 | redistribute, and sell modified and unmodified copies of the Font 44 | Software, subject to the following conditions: 45 | 46 | 1) Neither the Font Software nor any of its individual components, 47 | in Original or Modified Versions, may be sold by itself. 48 | 49 | 2) Original or Modified Versions of the Font Software may be bundled, 50 | redistributed and/or sold with any software, provided that each copy 51 | contains the above copyright notice and this license. These can be 52 | included either as stand-alone text files, human-readable headers or 53 | in the appropriate machine-readable metadata fields within text or 54 | binary files as long as those fields can be easily viewed by the user. 55 | 56 | 3) No Modified Version of the Font Software may use the Reserved Font 57 | Name(s) unless explicit written permission is granted by the corresponding 58 | Copyright Holder. This restriction only applies to the primary font name as 59 | presented to the users. 60 | 61 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 62 | Software shall not be used to promote, endorse or advertise any 63 | Modified Version, except to acknowledge the contribution(s) of the 64 | Copyright Holder(s) and the Author(s) or with their explicit written 65 | permission. 66 | 67 | 5) The Font Software, modified or unmodified, in part or in whole, 68 | must be distributed entirely under this license, and must not be 69 | distributed under any other license. The requirement for fonts to 70 | remain under this license does not apply to any document created 71 | using the Font Software. 72 | 73 | TERMINATION 74 | This license becomes null and void if any of the above conditions are 75 | not met. 76 | 77 | DISCLAIMER 78 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 79 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 80 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 81 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 82 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 83 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 84 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 85 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 86 | OTHER DEALINGS IN THE FONT SOFTWARE. 87 | -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/css/open-iconic/README.md: -------------------------------------------------------------------------------- 1 | [Open Iconic v1.1.1](http://useiconic.com/open) 2 | =========== 3 | 4 | ### Open Iconic is the open source sibling of [Iconic](http://useiconic.com). It is a hyper-legible collection of 223 icons with a tiny footprint—ready to use with Bootstrap and Foundation. [View the collection](http://useiconic.com/open#icons) 5 | 6 | 7 | 8 | ## What's in Open Iconic? 9 | 10 | * 223 icons designed to be legible down to 8 pixels 11 | * Super-light SVG files - 61.8 for the entire set 12 | * SVG sprite—the modern replacement for icon fonts 13 | * Webfont (EOT, OTF, SVG, TTF, WOFF), PNG and WebP formats 14 | * Webfont stylesheets (including versions for Bootstrap and Foundation) in CSS, LESS, SCSS and Stylus formats 15 | * PNG and WebP raster images in 8px, 16px, 24px, 32px, 48px and 64px. 16 | 17 | 18 | ## Getting Started 19 | 20 | #### For code samples and everything else you need to get started with Open Iconic, check out our [Icons](http://useiconic.com/open#icons) and [Reference](http://useiconic.com/open#reference) sections. 21 | 22 | ### General Usage 23 | 24 | #### Using Open Iconic's SVGs 25 | 26 | We like SVGs and we think they're the way to display icons on the web. Since Open Iconic are just basic SVGs, we suggest you display them like you would any other image (don't forget the `alt` attribute). 27 | 28 | ``` 29 | icon name 30 | ``` 31 | 32 | #### Using Open Iconic's SVG Sprite 33 | 34 | Open Iconic also comes in a SVG sprite which allows you to display all the icons in the set with a single request. It's like an icon font, without being a hack. 35 | 36 | Adding an icon from an SVG sprite is a little different than what you're used to, but it's still a piece of cake. *Tip: To make your icons easily style able, we suggest adding a general class to the* `` *tag and a unique class name for each different icon in the* `` *tag.* 37 | 38 | ``` 39 | 40 | 41 | 42 | ``` 43 | 44 | Sizing icons only needs basic CSS. All the icons are in a square format, so just set the `` tag with equal width and height dimensions. 45 | 46 | ``` 47 | .icon { 48 | width: 16px; 49 | height: 16px; 50 | } 51 | ``` 52 | 53 | Coloring icons is even easier. All you need to do is set the `fill` rule on the `` tag. 54 | 55 | ``` 56 | .icon-account-login { 57 | fill: #f00; 58 | } 59 | ``` 60 | 61 | To learn more about SVG Sprites, read [Chris Coyier's guide](http://css-tricks.com/svg-sprites-use-better-icon-fonts/). 62 | 63 | #### Using Open Iconic's Icon Font... 64 | 65 | 66 | ##### …with Bootstrap 67 | 68 | You can find our Bootstrap stylesheets in `font/css/open-iconic-bootstrap.{css, less, scss, styl}` 69 | 70 | 71 | ``` 72 | 73 | ``` 74 | 75 | 76 | ``` 77 | 78 | ``` 79 | 80 | ##### …with Foundation 81 | 82 | You can find our Foundation stylesheets in `font/css/open-iconic-foundation.{css, less, scss, styl}` 83 | 84 | ``` 85 | 86 | ``` 87 | 88 | 89 | ``` 90 | 91 | ``` 92 | 93 | ##### …on its own 94 | 95 | You can find our default stylesheets in `font/css/open-iconic.{css, less, scss, styl}` 96 | 97 | ``` 98 | 99 | ``` 100 | 101 | ``` 102 | 103 | ``` 104 | 105 | 106 | ## License 107 | 108 | ### Icons 109 | 110 | All code (including SVG markup) is under the [MIT License](http://opensource.org/licenses/MIT). 111 | 112 | ### Fonts 113 | 114 | All fonts are under the [SIL Licensed](http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web). 115 | -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KristofferStrube/Blazor.Streams/7c3b01eb79ce7a5d397f32cbbbf40fe29dd4f30c/samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KristofferStrube/Blazor.Streams/7c3b01eb79ce7a5d397f32cbbbf40fe29dd4f30c/samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KristofferStrube/Blazor.Streams/7c3b01eb79ce7a5d397f32cbbbf40fe29dd4f30c/samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KristofferStrube/Blazor.Streams/7c3b01eb79ce7a5d397f32cbbbf40fe29dd4f30c/samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KristofferStrube/Blazor.Streams/7c3b01eb79ce7a5d397f32cbbbf40fe29dd4f30c/samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/favicon.png -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KristofferStrube/Blazor.Streams/7c3b01eb79ce7a5d397f32cbbbf40fe29dd4f30c/samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/icon-192.png -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/images/mountain.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KristofferStrube/Blazor.Streams/7c3b01eb79ce7a5d397f32cbbbf40fe29dd4f30c/samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/images/mountain.jpg -------------------------------------------------------------------------------- /samples/KristofferStrube.Blazor.Streams.WasmExample/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Blazor Streams 8 | 9 | 10 | 32 | 43 | 44 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
61 | 62 | 63 | 64 | 65 |
66 |
67 | 68 |
69 | An unhandled error has occurred. 70 | Reload 71 | 🗙 72 |
73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/ArrayBufferView.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | 3 | namespace KristofferStrube.Blazor.Streams; 4 | 5 | /// 6 | /// WebIDL browser specs 7 | /// 8 | [Obsolete("A richer implementation has been introduced in Blazor.WebIDL so we shouldn't use this simple wrapper anymore. Use IArrayBufferView from Blazor.WebIDL instead.")] 9 | public class ArrayBufferView 10 | { 11 | public readonly IJSObjectReference JSReference; 12 | 13 | [Obsolete("A richer implementation has been introduced in Blazor.WebIDL so we shouldn't use this simple wrapper anymore. Use IArrayBufferView from Blazor.WebIDL instead.")] 14 | public static async Task CreateByteArrayAsync(IJSRuntime jSRuntime, int size) 15 | { 16 | IJSObjectReference helper = await jSRuntime.GetHelperAsync(); 17 | IJSObjectReference jSInstance = await helper.InvokeAsync("constructByteArray", size); 18 | return new ArrayBufferView(jSInstance); 19 | } 20 | 21 | [Obsolete("A richer implementation has been introduced in Blazor.WebIDL so we shouldn't use this simple wrapper anymore. Use IArrayBufferView from Blazor.WebIDL instead.")] 22 | protected internal ArrayBufferView(IJSObjectReference jSReference) 23 | { 24 | JSReference = jSReference; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/BaseJSStreamableWrapper.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// A base class for all streamable wrappers in Blazor.Streams. 8 | /// 9 | public abstract class BaseJSStreamableWrapper : Stream, IAsyncDisposable, IJSWrapper 10 | { 11 | /// 12 | /// A lazily loaded task that evaluates to a helper module instance from the Blazor.Streams library. 13 | /// 14 | protected readonly Lazy> helperTask; 15 | 16 | /// 17 | public IJSRuntime JSRuntime { get; } 18 | 19 | /// 20 | public IJSObjectReference JSReference { get; } 21 | 22 | /// 23 | public bool DisposesJSReference { get; } 24 | 25 | /// 26 | protected internal BaseJSStreamableWrapper(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) 27 | { 28 | helperTask = new(() => jSRuntime.GetHelperAsync()); 29 | JSRuntime = jSRuntime; 30 | JSReference = jSReference; 31 | DisposesJSReference = options.DisposesJSReference; 32 | } 33 | 34 | public override async ValueTask DisposeAsync() 35 | { 36 | await base.DisposeAsync(); 37 | if (helperTask.IsValueCreated) 38 | { 39 | IJSObjectReference module = await helperTask.Value; 40 | await module.DisposeAsync(); 41 | } 42 | GC.SuppressFinalize(this); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/BaseJSWrapper.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// A base class for all wrappers in Blazor.Streams. 8 | /// 9 | public abstract class BaseJSWrapper : IJSWrapper 10 | { 11 | /// 12 | /// A lazily loaded task that evaluates to a helper module instance from the Blazor.Streams library. 13 | /// 14 | protected readonly Lazy> helperTask; 15 | 16 | /// 17 | public IJSRuntime JSRuntime { get; } 18 | /// 19 | public IJSObjectReference JSReference { get; } 20 | /// 21 | public bool DisposesJSReference { get; } 22 | 23 | /// 24 | /// Constructs a wrapper instance for an equivalent JS instance. 25 | /// 26 | /// An instance. 27 | /// A JS reference to an existing JS instance that should be wrapped. 28 | /// The options for constructing this wrapper. 29 | internal BaseJSWrapper(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) 30 | { 31 | helperTask = new(() => jSRuntime.GetHelperAsync()); 32 | JSRuntime = jSRuntime; 33 | JSReference = jSReference; 34 | DisposesJSReference = options.DisposesJSReference; 35 | } 36 | 37 | /// 38 | public async ValueTask DisposeAsync() 39 | { 40 | if (helperTask.IsValueCreated) 41 | { 42 | IJSObjectReference module = await helperTask.Value; 43 | await module.DisposeAsync(); 44 | } 45 | await IJSWrapper.DisposeJSReference(this); 46 | GC.SuppressFinalize(this); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/EnumDescriptionConverter.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Reflection; 3 | using System.Text.Json; 4 | using System.Text.Json.Serialization; 5 | 6 | namespace KristofferStrube.Blazor.Streams; 7 | 8 | #pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. 9 | #pragma warning disable CS8603 // Possible null reference return. 10 | #pragma warning disable CS8604 // Possible null reference argument. 11 | #pragma warning disable CS8602 // Dereference of a possibly null reference. 12 | internal class EnumDescriptionConverter : JsonConverter where T : IComparable, IFormattable, IConvertible 13 | { 14 | public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 15 | { 16 | string jsonValue = reader.GetString(); 17 | 18 | foreach (FieldInfo? fi in typeToConvert.GetFields()) 19 | { 20 | var description = (DescriptionAttribute)fi.GetCustomAttribute(typeof(DescriptionAttribute), false); 21 | 22 | if (description != null) 23 | { 24 | if (description.Description == jsonValue) 25 | { 26 | return (T)fi.GetValue(null); 27 | } 28 | } 29 | } 30 | throw new JsonException($"string {jsonValue} was not found as a description in the enum {typeToConvert}"); 31 | } 32 | 33 | public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) 34 | { 35 | FieldInfo fi = value.GetType().GetField(value.ToString()); 36 | 37 | var description = (DescriptionAttribute)fi.GetCustomAttribute(typeof(DescriptionAttribute), false); 38 | 39 | writer.WriteStringValue(description.Description); 40 | } 41 | } 42 | #pragma warning restore CS8602 // Dereference of a possibly null reference. 43 | #pragma warning restore CS8604 // Possible null reference argument. 44 | #pragma warning restore CS8603 // Possible null reference return. 45 | #pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/Enums/ReadableStreamReaderMode.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | [JsonConverter(typeof(EnumDescriptionConverter))] 10 | public enum ReadableStreamReaderMode 11 | { 12 | [Description("byob")] 13 | Byob, 14 | } 15 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/Enums/ReadableStreamType.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | [JsonConverter(typeof(EnumDescriptionConverter))] 10 | public enum ReadableStreamType 11 | { 12 | [Description("bytes")] 13 | Bytes, 14 | } 15 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/Extensions/IJSRuntimeExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | 3 | namespace KristofferStrube.Blazor.Streams; 4 | 5 | internal static class IJSRuntimeExtensions 6 | { 7 | internal static async Task GetHelperAsync(this IJSRuntime jSRuntime) 8 | { 9 | return await jSRuntime.InvokeAsync( 10 | "import", "./_content/KristofferStrube.Blazor.Streams/KristofferStrube.Blazor.Streams.js"); 11 | } 12 | internal static async Task GetInProcessHelperAsync(this IJSRuntime jSRuntime) 13 | { 14 | return await jSRuntime.InvokeAsync( 15 | "import", "./_content/KristofferStrube.Blazor.Streams/KristofferStrube.Blazor.Streams.js"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/KristofferStrube.Blazor.Streams.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0;net8.0 5 | enable 6 | enable 7 | Blazor Streams wrapper 8 | Streams wrapper implementation for Blazor. 9 | KristofferStrube.Blazor.Streams 10 | Blazor;Wasm;Wrapper;Streams;File;FileSystem;ReadableStream;WritableStream;TransformStream;JSInterop; 11 | https://github.com/KristofferStrube/Blazor.Streams 12 | git 13 | MIT 14 | 0.5.0 15 | Kristoffer Strube 16 | README.md 17 | icon.png 18 | true 19 | 20 | 21 | 22 | 23 | True 24 | \ 25 | 26 | 27 | True 28 | \ 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/Options/ByteLengthQueuingStrategy.InProcess.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class ByteLengthQueuingStrategyInProcess : ByteLengthQueuingStrategy, IJSInProcessCreatable 10 | { 11 | /// 12 | /// An in-process helper module instance from the Blazor.Streams library. 13 | /// 14 | protected readonly IJSInProcessObjectReference inProcessHelper; 15 | 16 | /// 17 | public new IJSInProcessObjectReference JSReference { get; } 18 | 19 | /// 20 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference) 21 | { 22 | return await CreateAsync(jSRuntime, jSReference, new()); 23 | } 24 | 25 | /// 26 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference, CreationOptions options) 27 | { 28 | IJSInProcessObjectReference inProcesshelper = await jSRuntime.GetInProcessHelperAsync(); 29 | return new ByteLengthQueuingStrategyInProcess(jSRuntime, inProcesshelper, jSReference, options); 30 | } 31 | 32 | /// 33 | /// Constructs a wrapper instance using the standard constructor. 34 | /// 35 | /// An IJSRuntime instance. 36 | /// An that provides the required . 37 | /// A wrapper instance for a . 38 | public static new async Task CreateAsync(IJSRuntime jSRuntime, QueuingStrategyInit init) 39 | { 40 | IJSInProcessObjectReference inProcessHelper = await jSRuntime.GetInProcessHelperAsync(); 41 | IJSInProcessObjectReference jSInstance = await inProcessHelper.InvokeAsync("constructByteLengthQueuingStrategy", init); 42 | return new ByteLengthQueuingStrategyInProcess(jSRuntime, inProcessHelper, jSInstance, new() { DisposesJSReference = true }); 43 | } 44 | 45 | /// 46 | protected ByteLengthQueuingStrategyInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference inProcessHelper, IJSInProcessObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) 47 | { 48 | this.inProcessHelper = inProcessHelper; 49 | JSReference = jSReference; 50 | } 51 | 52 | public double HighWaterMark => inProcessHelper.Invoke("getAttribute", JSReference, "highWaterMark"); 53 | 54 | public double Size(IJSObjectReference chunk) 55 | { 56 | return JSReference.Invoke("size", chunk); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/Options/ByteLengthQueuingStrategy.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class ByteLengthQueuingStrategy : BaseJSWrapper, IJSCreatable 10 | { 11 | /// 12 | /// Constructs a wrapper instance for a given JS Instance of a . 13 | /// 14 | /// An instance. 15 | /// A JS reference to an existing . 16 | /// A wrapper instance for a . 17 | [Obsolete("This will be removed in the next major release as all creator methods should be asynchronous for uniformity. Use CreateAsync instead.")] 18 | public static ByteLengthQueuingStrategy Create(IJSRuntime jSRuntime, IJSObjectReference jSReference) 19 | { 20 | return new ByteLengthQueuingStrategy(jSRuntime, jSReference, new()); 21 | } 22 | 23 | /// 24 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference) 25 | { 26 | return await CreateAsync(jSRuntime, jSReference, new()); 27 | } 28 | 29 | /// 30 | public static Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) 31 | { 32 | return Task.FromResult(new ByteLengthQueuingStrategy(jSRuntime, jSReference, options)); 33 | } 34 | 35 | /// 36 | /// Constructs a wrapper instance using the standard constructor. 37 | /// 38 | /// An IJSRuntime instance. 39 | /// An that provides the required . 40 | /// A wrapper instance for a . 41 | public static async Task CreateAsync(IJSRuntime jSRuntime, QueuingStrategyInit init) 42 | { 43 | IJSObjectReference helper = await jSRuntime.GetHelperAsync(); 44 | IJSObjectReference jSInstance = await helper.InvokeAsync("constructByteLengthQueuingStrategy", init); 45 | return new ByteLengthQueuingStrategy(jSRuntime, jSInstance, new() { DisposesJSReference = true }); 46 | } 47 | 48 | /// 49 | protected ByteLengthQueuingStrategy(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) { } 50 | 51 | public async Task GetHighWaterMarkAsync() 52 | { 53 | IJSObjectReference helper = await helperTask.Value; 54 | return await helper.InvokeAsync("getAttribute", JSReference, "highWaterMark"); 55 | } 56 | 57 | public async Task SizeAsync(IJSObjectReference chunk) 58 | { 59 | return await JSReference.InvokeAsync("size", chunk); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/Options/CountQueuingStrategy.InProcess.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class CountQueuingStrategyInProcess : CountQueuingStrategy 10 | { 11 | /// 12 | /// An in-process helper module instance from the Blazor.Streams library. 13 | /// 14 | protected readonly IJSInProcessObjectReference inProcessHelper; 15 | 16 | /// 17 | public new IJSInProcessObjectReference JSReference { get; } 18 | 19 | /// 20 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference) 21 | { 22 | return await CreateAsync(jSRuntime, jSReference, new()); 23 | } 24 | 25 | /// 26 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference, CreationOptions options) 27 | { 28 | IJSInProcessObjectReference inProcesshelper = await jSRuntime.GetInProcessHelperAsync(); 29 | return new CountQueuingStrategyInProcess(jSRuntime, inProcesshelper, jSReference, options); 30 | } 31 | 32 | /// 33 | /// Constructs a wrapper instance using the standard constructor. 34 | /// 35 | /// An IJSRuntime instance. 36 | /// An that provides the required . 37 | /// A wrapper instance for a . 38 | public static new async Task CreateAsync(IJSRuntime jSRuntime, QueuingStrategyInit init) 39 | { 40 | IJSInProcessObjectReference inProcessHelper = await jSRuntime.GetInProcessHelperAsync(); 41 | IJSInProcessObjectReference jSInstance = await inProcessHelper.InvokeAsync("constructCountQueuingStrategy", init); 42 | return new CountQueuingStrategyInProcess(jSRuntime, inProcessHelper, jSInstance, new() { DisposesJSReference = true }); 43 | } 44 | 45 | /// 46 | protected CountQueuingStrategyInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference inProcessHelper, IJSInProcessObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) 47 | { 48 | this.inProcessHelper = inProcessHelper; 49 | JSReference = jSReference; 50 | } 51 | 52 | public double HighWaterMark => inProcessHelper.Invoke("getAttribute", JSReference, "highWaterMark"); 53 | 54 | public double Size(IJSObjectReference chunk) 55 | { 56 | return JSReference.Invoke("size", chunk); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/Options/CountQueuingStrategy.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class CountQueuingStrategy : BaseJSWrapper, IJSCreatable 10 | { 11 | /// 12 | /// Constructs a wrapper instance for a given JS Instance of a . 13 | /// 14 | /// An instance. 15 | /// A JS reference to an existing . 16 | /// A wrapper instance for a . 17 | [Obsolete("This will be removed in the next major release as all creator methods should be asynchronous for uniformity. Use CreateAsync instead.")] 18 | public static CountQueuingStrategy Create(IJSRuntime jSRuntime, IJSObjectReference jSReference) 19 | { 20 | return new CountQueuingStrategy(jSRuntime, jSReference, new()); 21 | } 22 | 23 | /// 24 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference) 25 | { 26 | return await CreateAsync(jSRuntime, jSReference, new()); 27 | } 28 | 29 | /// 30 | public static Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) 31 | { 32 | return Task.FromResult(new CountQueuingStrategy(jSRuntime, jSReference, options)); 33 | } 34 | 35 | /// 36 | /// Constructs a wrapper instance using the standard constructor. 37 | /// 38 | /// An IJSRuntime instance. 39 | /// An that provides the required . 40 | /// A wrapper instance for a . 41 | public static async Task CreateAsync(IJSRuntime jSRuntime, QueuingStrategyInit init) 42 | { 43 | IJSObjectReference helper = await jSRuntime.GetHelperAsync(); 44 | IJSObjectReference jSInstance = await helper.InvokeAsync("constructCountQueuingStrategy", init); 45 | return new CountQueuingStrategy(jSRuntime, jSInstance, new() { DisposesJSReference = true }); 46 | } 47 | 48 | /// 49 | protected CountQueuingStrategy(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) { } 50 | 51 | public async Task GetHighWaterMarkAsync() 52 | { 53 | IJSObjectReference helper = await helperTask.Value; 54 | return await helper.InvokeAsync("getAttribute", JSReference, "highWaterMark"); 55 | } 56 | 57 | public async Task SizeAsync(IJSObjectReference chunk) 58 | { 59 | return await JSReference.InvokeAsync("size", chunk); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/Options/QueuingStrategy.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class QueuingStrategy 10 | { 11 | public QueuingStrategy() 12 | { 13 | ObjRef = DotNetObjectReference.Create(this); 14 | } 15 | 16 | public DotNetObjectReference ObjRef { get; set; } 17 | 18 | [JsonPropertyName("highWaterMark")] 19 | public double HighWaterMark { get; set; } 20 | 21 | [JsonIgnore] 22 | public Func? Size { get; set; } 23 | 24 | [JSInvokable] 25 | public double InvokeSize(IJSObjectReference chunk) 26 | { 27 | return Size is null ? 0 : Size.Invoke(chunk); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/Options/QueuingStrategyInit.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace KristofferStrube.Blazor.Streams; 4 | 5 | /// 6 | /// Streams browser specs 7 | /// 8 | public class QueuingStrategyInit 9 | { 10 | public QueuingStrategyInit(double highWaterMark) 11 | { 12 | HighWaterMark = highWaterMark; 13 | } 14 | 15 | [JsonPropertyName("highWaterMark")] 16 | public double HighWaterMark { get; set; } 17 | } 18 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/Options/ReadableStreamBYOBReaderReadOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace KristofferStrube.Blazor.Streams; 4 | 5 | /// 6 | /// Streams browser specs 7 | /// 8 | public class ReadableStreamBYOBReaderReadOptions 9 | { 10 | /// 11 | /// The minimal number of elements needed to be read for the to finish. 12 | /// 13 | [JsonPropertyName("min")] 14 | public ulong Min { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/Options/ReadableStreamGetReaderOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace KristofferStrube.Blazor.Streams; 4 | 5 | /// 6 | /// Streams browser specs 7 | /// 8 | public class ReadableStreamGetReaderOptions 9 | { 10 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] 11 | [JsonPropertyName("mode")] 12 | public ReadableStreamReaderMode Mode { get; set; } 13 | } 14 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/Options/StreamPipeOptions.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.DOM; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class StreamPipeOptions 10 | { 11 | /// 12 | /// Decides whether the destination will be closed when the is closed. 13 | /// 14 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] 15 | [JsonPropertyName("preventClose")] 16 | public bool PreventClose { get; set; } 17 | 18 | /// 19 | /// Decides whether the destination will be aborted when the is aborted. 20 | /// 21 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] 22 | [JsonPropertyName("preventAbort")] 23 | public bool PreventAbort { get; set; } 24 | 25 | /// 26 | /// Decides whether the will be aborted when the destination is aborted. 27 | /// 28 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] 29 | [JsonPropertyName("preventCancel")] 30 | public bool PreventCancel { get; set; } 31 | 32 | /// 33 | /// The signal option can be set to an to allow aborting an ongoing pipe operation via the corresponding . 34 | /// In this case, this source readable stream will be canceled, and destination aborted, unless the respective options or are set. 35 | /// 36 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] 37 | [JsonPropertyName("signal")] 38 | public AbortSignal? Signal { get; set; } 39 | } 40 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/Options/Transformer.InProcess.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class TransformerInProcess : Transformer 10 | { 11 | /// 12 | /// An in-process helper module instance from the Blazor.Streams library. 13 | /// 14 | protected readonly IJSInProcessObjectReference inProcessHelper; 15 | 16 | /// 17 | /// Constructs a wrapper instance. 18 | /// 19 | /// An instance. 20 | /// A new wrapper instance for a . 21 | public static new async Task CreateAsync(IJSRuntime jSRuntime) 22 | { 23 | IJSInProcessObjectReference inProcessHelper = await jSRuntime.GetInProcessHelperAsync(); 24 | return new TransformerInProcess(jSRuntime, inProcessHelper); 25 | } 26 | 27 | /// 28 | /// Constructs a wrapper instance. 29 | /// 30 | /// An instance. 31 | /// An in process helper instance. 32 | protected TransformerInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference inProcessHelper) : base(jSRuntime) 33 | { 34 | this.inProcessHelper = inProcessHelper; 35 | ObjRef = DotNetObjectReference.Create(this); 36 | } 37 | 38 | public new DotNetObjectReference ObjRef { get; init; } 39 | 40 | /// 41 | /// A function that is called immediately during creation of the . 42 | /// 43 | [JsonIgnore] 44 | public new Action? Start { get; set; } 45 | 46 | /// 47 | /// A function called when a new chunk originally written to the writable side is ready to be transformed. 48 | /// 49 | [JsonIgnore] 50 | public new Action? Transform { get; set; } 51 | 52 | /// 53 | /// A function called after all chunks written to the writable side have been transformed by successfully passing through , and the writable side is about to be closed. 54 | /// 55 | [JsonIgnore] 56 | public new Action? Flush { get; set; } 57 | 58 | /// 59 | /// A function called when the readable side is cancelled, or when the writable side is aborted. 60 | /// 61 | [JsonIgnore] 62 | public new Action? Cancel { get; set; } 63 | 64 | [JSInvokable] 65 | public void InvokeStart(IJSInProcessObjectReference controller) 66 | { 67 | if (Start is null) 68 | { 69 | return; 70 | } 71 | 72 | Start.Invoke(new TransformStreamDefaultControllerInProcess(jSRuntime, inProcessHelper, controller, new() { DisposesJSReference = true })); 73 | } 74 | 75 | [JSInvokable] 76 | public void InvokeTransform(IJSObjectReference chunk, IJSInProcessObjectReference controller) 77 | { 78 | if (Transform is null) 79 | { 80 | return; 81 | } 82 | 83 | Transform.Invoke(chunk, new TransformStreamDefaultControllerInProcess(jSRuntime, inProcessHelper, controller, new() { DisposesJSReference = true })); 84 | } 85 | 86 | [JSInvokable] 87 | public void InvokeFlush(IJSInProcessObjectReference controller) 88 | { 89 | if (Flush is null) 90 | { 91 | return; 92 | } 93 | 94 | Flush.Invoke(new TransformStreamDefaultControllerInProcess(jSRuntime, inProcessHelper, controller, new() { DisposesJSReference = true })); 95 | } 96 | 97 | [JSInvokable] 98 | public void InvokeCancel(IJSInProcessObjectReference reason) 99 | { 100 | if (Cancel is null) 101 | { 102 | return; 103 | } 104 | 105 | Cancel.Invoke(reason); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/Options/Transformer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class Transformer : IDisposable 10 | { 11 | protected readonly Lazy> helperTask; 12 | protected readonly IJSRuntime jSRuntime; 13 | 14 | /// 15 | /// Constructs a wrapper instance. 16 | /// 17 | /// An instance. 18 | /// A new wrapper instance. 19 | [Obsolete("This will be removed in the next major release as all creator methods should be asynchronous for uniformity. Use CreateAsync instead.")] 20 | public static Transformer Create(IJSRuntime jSRuntime) 21 | { 22 | return new Transformer(jSRuntime); 23 | } 24 | 25 | /// 26 | /// Constructs a wrapper instance. 27 | /// 28 | /// An instance. 29 | /// A new wrapper instance. 30 | public static Task CreateAsync(IJSRuntime jSRuntime) 31 | { 32 | return Task.FromResult(new Transformer(jSRuntime)); 33 | } 34 | 35 | /// 36 | /// Constructs a wrapper instance. 37 | /// 38 | /// An instance. 39 | protected Transformer(IJSRuntime jSRuntime) 40 | { 41 | helperTask = new(jSRuntime.GetHelperAsync); 42 | this.jSRuntime = jSRuntime; 43 | ObjRef = DotNetObjectReference.Create(this); 44 | } 45 | 46 | public DotNetObjectReference ObjRef { get; init; } 47 | 48 | /// 49 | /// A function that is called immediately during creation of the . 50 | /// 51 | [JsonIgnore] 52 | public Func? Start { get; set; } 53 | 54 | /// 55 | /// A function called when a new chunk originally written to the writable side is ready to be transformed. 56 | /// 57 | [JsonIgnore] 58 | public Func? Transform { get; set; } 59 | 60 | /// 61 | /// A function called after all chunks written to the writable side have been transformed by successfully passing through , and the writable side is about to be closed. 62 | /// 63 | [JsonIgnore] 64 | public Func? Flush { get; set; } 65 | 66 | /// 67 | /// A function called when the readable side is cancelled, or when the writable side is aborted. 68 | /// 69 | [JsonIgnore] 70 | public Func? Cancel { get; set; } 71 | 72 | [JSInvokable] 73 | public async Task InvokeStart(IJSObjectReference controller) 74 | { 75 | if (Start is null) 76 | { 77 | return; 78 | } 79 | 80 | await Start.Invoke(new TransformStreamDefaultController(jSRuntime, controller, new() { DisposesJSReference = true })); 81 | } 82 | 83 | [JSInvokable] 84 | public async Task InvokeTransform(IJSObjectReference chunk, IJSObjectReference controller) 85 | { 86 | if (Transform is null) 87 | { 88 | return; 89 | } 90 | 91 | await Transform.Invoke(chunk, new TransformStreamDefaultController(jSRuntime, controller, new() { DisposesJSReference = true })); 92 | } 93 | 94 | [JSInvokable] 95 | public async Task InvokeFlush(IJSObjectReference controller) 96 | { 97 | if (Flush is null) 98 | { 99 | return; 100 | } 101 | 102 | await Flush.Invoke(new TransformStreamDefaultController(jSRuntime, controller, new() { DisposesJSReference = true })); 103 | } 104 | 105 | [JSInvokable] 106 | public async Task InvokeCancel(IJSObjectReference reason) 107 | { 108 | if (Cancel is null) 109 | { 110 | return; 111 | } 112 | 113 | await Cancel.Invoke(reason); 114 | } 115 | 116 | public void Dispose() 117 | { 118 | ObjRef.Dispose(); 119 | GC.SuppressFinalize(this); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/Options/UnderlyingSink.InProcess.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class UnderlyingSinkInProcess : UnderlyingSink 10 | { 11 | /// 12 | /// An in-process helper module instance from the Blazor.Streams library. 13 | /// 14 | protected readonly IJSInProcessObjectReference inProcessHelper; 15 | 16 | /// 17 | /// Constructs a wrapper instance. 18 | /// 19 | /// An instance. 20 | /// A new wrapper instance for a . 21 | public static new async Task CreateAsync(IJSRuntime jSRuntime) 22 | { 23 | IJSInProcessObjectReference inProcessHelper = await jSRuntime.GetInProcessHelperAsync(); 24 | return new UnderlyingSinkInProcess(jSRuntime, inProcessHelper); 25 | } 26 | 27 | /// 28 | /// Constructs a wrapper instance. 29 | /// 30 | /// An instance. 31 | /// An in process helper instance. 32 | protected UnderlyingSinkInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference inProcessHelper) : base(jSRuntime) 33 | { 34 | this.inProcessHelper = inProcessHelper; 35 | ObjRef = DotNetObjectReference.Create(this); 36 | } 37 | 38 | public new DotNetObjectReference ObjRef { get; init; } 39 | 40 | [JsonIgnore] 41 | public new Action? Start { get; set; } 42 | 43 | [JsonIgnore] 44 | public new Action? Write { get; set; } 45 | 46 | [JsonIgnore] 47 | public new Action? Close { get; set; } 48 | 49 | [JsonIgnore] 50 | public new Action? Abort { get; set; } 51 | 52 | [JSInvokable] 53 | public void InvokeStart(IJSInProcessObjectReference controller) 54 | { 55 | if (Start is null) 56 | { 57 | return; 58 | } 59 | 60 | Start.Invoke(new WritableStreamDefaultControllerInProcess(jSRuntime, inProcessHelper, controller, new() { DisposesJSReference = true })); 61 | } 62 | 63 | [JSInvokable] 64 | public void InvokeWrite(IJSObjectReference chunk, IJSInProcessObjectReference controller) 65 | { 66 | if (Write is null) 67 | { 68 | return; 69 | } 70 | 71 | Write.Invoke(chunk, new WritableStreamDefaultControllerInProcess(jSRuntime, inProcessHelper, controller, new() { DisposesJSReference = true })); 72 | } 73 | 74 | [JSInvokable] 75 | public new void InvokeClose() 76 | { 77 | if (Close is null) 78 | { 79 | return; 80 | } 81 | 82 | Close.Invoke(); 83 | } 84 | 85 | [JSInvokable] 86 | public new void InvokeAbort() 87 | { 88 | if (Abort is null) 89 | { 90 | return; 91 | } 92 | 93 | Abort.Invoke(); 94 | } 95 | 96 | public new void Dispose() 97 | { 98 | ObjRef.Dispose(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/Options/UnderlyingSink.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class UnderlyingSink : IDisposable 10 | { 11 | protected readonly Lazy> helperTask; 12 | protected readonly IJSRuntime jSRuntime; 13 | 14 | /// 15 | /// Constructs a wrapper instance. 16 | /// 17 | /// An instance. 18 | /// A new wrapper instance. 19 | [Obsolete("This will be removed in the next major release as all creator methods should be asynchronous for uniformity. Use CreateAsync instead.")] 20 | public static UnderlyingSink Create(IJSRuntime jSRuntime) 21 | { 22 | return new UnderlyingSink(jSRuntime); 23 | } 24 | 25 | /// 26 | /// Constructs a wrapper instance. 27 | /// 28 | /// An instance. 29 | /// A new wrapper instance. 30 | public static Task CreateAsync(IJSRuntime jSRuntime) 31 | { 32 | return Task.FromResult(new UnderlyingSink(jSRuntime)); 33 | } 34 | 35 | /// 36 | /// Constructs a wrapper instance. 37 | /// 38 | /// An instance. 39 | public UnderlyingSink(IJSRuntime jSRuntime) 40 | { 41 | helperTask = new(() => jSRuntime.GetHelperAsync()); 42 | this.jSRuntime = jSRuntime; 43 | ObjRef = DotNetObjectReference.Create(this); 44 | } 45 | 46 | public DotNetObjectReference ObjRef { get; init; } 47 | 48 | [JsonIgnore] 49 | public Func? Start { get; set; } 50 | 51 | [JsonIgnore] 52 | public Func? Write { get; set; } 53 | 54 | [JsonIgnore] 55 | public Func? Close { get; set; } 56 | 57 | [JsonIgnore] 58 | public Func? Abort { get; set; } 59 | 60 | [JSInvokable] 61 | public async Task InvokeStart(IJSObjectReference controller) 62 | { 63 | if (Start is null) 64 | { 65 | return; 66 | } 67 | 68 | await Start.Invoke(new WritableStreamDefaultController(jSRuntime, controller, new() { DisposesJSReference = true })); 69 | } 70 | 71 | [JSInvokable] 72 | public async Task InvokeWrite(IJSObjectReference chunk, IJSObjectReference controller) 73 | { 74 | if (Write is null) 75 | { 76 | return; 77 | } 78 | 79 | await Write.Invoke(chunk, new WritableStreamDefaultController(jSRuntime, controller, new() { DisposesJSReference = true })); 80 | } 81 | 82 | [JSInvokable] 83 | public async Task InvokeClose() 84 | { 85 | if (Close is null) 86 | { 87 | return; 88 | } 89 | 90 | await Close.Invoke(); 91 | } 92 | 93 | [JSInvokable] 94 | public async Task InvokeAbort() 95 | { 96 | if (Abort is null) 97 | { 98 | return; 99 | } 100 | 101 | await Abort.Invoke(); 102 | } 103 | 104 | public void Dispose() 105 | { 106 | ObjRef.Dispose(); 107 | GC.SuppressFinalize(this); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/Options/UnderlyingSource.InProcess.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class UnderlyingSourceInProcess : UnderlyingSource 10 | { 11 | /// 12 | /// An in-process helper module instance from the Blazor.Streams library. 13 | /// 14 | protected readonly IJSInProcessObjectReference inProcessHelper; 15 | 16 | /// 17 | /// Constructs a wrapper instance. 18 | /// 19 | /// An instance. 20 | /// A new wrapper instance for a . 21 | public static new async Task CreateAsync(IJSRuntime jSRuntime) 22 | { 23 | IJSInProcessObjectReference inProcessHelper = await jSRuntime.GetInProcessHelperAsync(); 24 | return new UnderlyingSourceInProcess(jSRuntime, inProcessHelper); 25 | } 26 | 27 | /// 28 | /// Constructs a wrapper instance. 29 | /// 30 | /// An instance. 31 | /// An in process helper instance. 32 | protected UnderlyingSourceInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference inProcessHelper) : base(jSRuntime) 33 | { 34 | this.inProcessHelper = inProcessHelper; 35 | ObjRef = DotNetObjectReference.Create(this); 36 | } 37 | 38 | public new DotNetObjectReference ObjRef { get; init; } 39 | 40 | [JsonIgnore] 41 | public new Action? Start { get; set; } 42 | 43 | [JsonIgnore] 44 | public new Action? Pull { get; set; } 45 | 46 | [JsonIgnore] 47 | public new Action? Cancel { get; set; } 48 | 49 | [JSInvokable] 50 | public void InvokeStart(IJSInProcessObjectReference controller) 51 | { 52 | if (Type is ReadableStreamType.Bytes) 53 | { 54 | Start?.Invoke(new ReadableByteStreamControllerInProcess(jSRuntime, inProcessHelper, controller, new() { DisposesJSReference = true })); 55 | } 56 | else 57 | { 58 | Start?.Invoke(new ReadableStreamDefaultControllerInProcess(jSRuntime, inProcessHelper, controller, new() { DisposesJSReference = true })); 59 | } 60 | } 61 | 62 | [JSInvokable] 63 | public void InvokePull(IJSInProcessObjectReference controller) 64 | { 65 | if (Type is ReadableStreamType.Bytes) 66 | { 67 | Pull?.Invoke(new ReadableByteStreamControllerInProcess(jSRuntime, inProcessHelper, controller, new() { DisposesJSReference = true })); 68 | } 69 | else 70 | { 71 | Pull?.Invoke(new ReadableStreamDefaultControllerInProcess(jSRuntime, inProcessHelper, controller, new() { DisposesJSReference = true })); 72 | } 73 | } 74 | 75 | [JSInvokable] 76 | public new void InvokeCancel() 77 | { 78 | if (Type is ReadableStreamType.Bytes) 79 | { 80 | Cancel?.Invoke(); 81 | } 82 | else 83 | { 84 | Cancel?.Invoke(); 85 | } 86 | } 87 | 88 | public new void Dispose() 89 | { 90 | ObjRef.Dispose(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/Options/UnderlyingSource.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class UnderlyingSource : IDisposable 10 | { 11 | protected readonly Lazy> helperTask; 12 | protected readonly IJSRuntime jSRuntime; 13 | 14 | /// 15 | /// Constructs a wrapper instance. 16 | /// 17 | /// An instance. 18 | /// A new wrapper instance. 19 | [Obsolete("This will be removed in the next major release as all creator methods should be asynchronous for uniformity. Use CreateAsync instead.")] 20 | public static UnderlyingSource Create(IJSRuntime jSRuntime) 21 | { 22 | return new UnderlyingSource(jSRuntime); 23 | } 24 | 25 | /// 26 | /// Constructs a wrapper instance. 27 | /// 28 | /// An instance. 29 | /// A new wrapper instance. 30 | public static Task CreateAsync(IJSRuntime jSRuntime) 31 | { 32 | return Task.FromResult(new UnderlyingSource(jSRuntime)); 33 | } 34 | 35 | /// 36 | /// Constructs a wrapper instance. 37 | /// 38 | /// An instance. 39 | public UnderlyingSource(IJSRuntime jSRuntime) 40 | { 41 | helperTask = new(() => jSRuntime.GetHelperAsync()); 42 | this.jSRuntime = jSRuntime; 43 | ObjRef = DotNetObjectReference.Create(this); 44 | } 45 | 46 | public DotNetObjectReference ObjRef { get; init; } 47 | 48 | [JsonIgnore] 49 | public Func? Start { get; set; } 50 | 51 | [JsonIgnore] 52 | public Func? Pull { get; set; } 53 | 54 | [JsonIgnore] 55 | public Func? Cancel { get; set; } 56 | 57 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] 58 | [JsonPropertyName("type")] 59 | public ReadableStreamType? Type { get; set; } 60 | 61 | [JsonPropertyName("autoAllocateChunkSize")] 62 | public ulong AutoAllocateChunkSize { get; set; } 63 | 64 | [JSInvokable] 65 | public async Task InvokeStart(IJSObjectReference controller) 66 | { 67 | if (Start is null) 68 | { 69 | return; 70 | } 71 | 72 | if (Type is ReadableStreamType.Bytes) 73 | { 74 | await Start.Invoke(new ReadableByteStreamController(jSRuntime, controller, new() { DisposesJSReference = true })); 75 | } 76 | else 77 | { 78 | await Start.Invoke(new ReadableStreamDefaultController(jSRuntime, controller, new() { DisposesJSReference = true })); 79 | } 80 | } 81 | 82 | [JSInvokable] 83 | public async Task InvokePull(IJSObjectReference controller) 84 | { 85 | if (Pull is null) 86 | { 87 | return; 88 | } 89 | 90 | if (Type is ReadableStreamType.Bytes) 91 | { 92 | await Pull.Invoke(new ReadableByteStreamController(jSRuntime, controller, new() { DisposesJSReference = true })); 93 | } 94 | else 95 | { 96 | await Pull.Invoke(new ReadableStreamDefaultController(jSRuntime, controller, new() { DisposesJSReference = true })); 97 | } 98 | } 99 | 100 | [JSInvokable] 101 | public async Task InvokeCancel() 102 | { 103 | if (Cancel is null) 104 | { 105 | return; 106 | } 107 | 108 | if (Type is ReadableStreamType.Bytes) 109 | { 110 | await Cancel.Invoke(); 111 | } 112 | else 113 | { 114 | await Cancel.Invoke(); 115 | } 116 | } 117 | 118 | public void Dispose() 119 | { 120 | ObjRef.Dispose(); 121 | GC.SuppressFinalize(this); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/ReadableStream/ReadableByteStreamController.InProcess.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class ReadableByteStreamControllerInProcess : ReadableByteStreamController, IJSInProcessCreatable 10 | { 11 | /// 12 | /// An in-process helper module instance from the Blazor.Streams library. 13 | /// 14 | protected readonly IJSInProcessObjectReference inProcessHelper; 15 | 16 | /// 17 | public new IJSInProcessObjectReference JSReference { get; } 18 | 19 | /// 20 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference) 21 | { 22 | return await CreateAsync(jSRuntime, jSReference, new()); 23 | } 24 | 25 | /// 26 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference, CreationOptions options) 27 | { 28 | IJSInProcessObjectReference inProcesshelper = await jSRuntime.GetInProcessHelperAsync(); 29 | return new ReadableByteStreamControllerInProcess(jSRuntime, inProcesshelper, jSReference, options); 30 | } 31 | 32 | /// 33 | protected internal ReadableByteStreamControllerInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference inProcessHelper, IJSInProcessObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) 34 | { 35 | this.inProcessHelper = inProcessHelper; 36 | JSReference = jSReference; 37 | } 38 | 39 | /// 40 | /// Returns the current BYOB pull request, or null if there isn’t one. 41 | /// 42 | /// A 43 | public ReadableStreamBYOBRequestInProcess? BYOBRequest 44 | { 45 | get 46 | { 47 | IJSInProcessObjectReference? jSInstance = inProcessHelper.Invoke("getAttribute", JSReference, "byobRequest"); 48 | if (jSInstance is null) 49 | { 50 | return null; 51 | } 52 | return new ReadableStreamBYOBRequestInProcess(JSRuntime, inProcessHelper, jSInstance, new() { DisposesJSReference = true }); 53 | } 54 | } 55 | 56 | /// 57 | /// The desired size to fill the controlled stream's internal queue. 58 | /// 59 | /// Negative values means that the queue is overfull. 60 | public double? DesiredSize => inProcessHelper.Invoke("getAttribute", JSReference, "desiredSize"); 61 | 62 | /// 63 | /// Closes the controlled stream once all previously enqueued chunks have been read. 64 | /// 65 | public void Close() 66 | { 67 | JSReference.InvokeVoid("close"); 68 | } 69 | 70 | /// 71 | /// Enqueues the chunk in the controlled stream. 72 | /// 73 | /// An supplied as the BYOB. 74 | public void Enqueue(IArrayBufferView chunk) 75 | { 76 | JSReference.InvokeVoid("enqueue", chunk.JSReference); 77 | } 78 | 79 | /// 80 | /// Errors the controlled stream so that all future interactions will fail. 81 | /// 82 | public void Error() 83 | { 84 | JSReference.InvokeVoid("error"); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/ReadableStream/ReadableByteStreamController.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class ReadableByteStreamController : ReadableStreamController, IJSCreatable 10 | { 11 | /// 12 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference) 13 | { 14 | return await CreateAsync(jSRuntime, jSReference, new()); 15 | } 16 | 17 | /// 18 | public static Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) 19 | { 20 | return Task.FromResult(new ReadableByteStreamController(jSRuntime, jSReference, options)); 21 | } 22 | 23 | /// 24 | protected internal ReadableByteStreamController(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) { } 25 | 26 | /// 27 | /// Returns the current BYOB pull request, or null if there isn’t one. 28 | /// 29 | /// A 30 | public async Task GetBYOBRequestAsync() 31 | { 32 | IJSObjectReference helper = await helperTask.Value; 33 | IJSObjectReference? jSInstance = await helper.InvokeAsync("getAttribute", JSReference, "byobRequest"); 34 | if (jSInstance is null) 35 | { 36 | return null; 37 | } 38 | return new ReadableStreamBYOBRequest(JSRuntime, jSInstance, new() { DisposesJSReference = true }); 39 | } 40 | 41 | /// 42 | /// Enqueues the chunk in the controlled stream. 43 | /// 44 | /// An supplied as the BYOB. 45 | public async Task EnqueueAsync(IArrayBufferView chunk) 46 | { 47 | await JSReference.InvokeVoidAsync("enqueue", chunk.JSReference); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/ReadableStream/ReadableStream.Stream.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | 3 | namespace KristofferStrube.Blazor.Streams; 4 | 5 | public partial class ReadableStream 6 | { 7 | private ReadableStreamDefaultReader? reader; 8 | 9 | public override bool CanRead => true; 10 | 11 | public override bool CanSeek => false; 12 | 13 | public override bool CanWrite => false; 14 | 15 | /// 16 | /// We can't check the length of a . 17 | /// 18 | public override long Length => 0; 19 | 20 | public override long Position { get; set; } 21 | 22 | public override void Flush() 23 | { 24 | throw new InvalidOperationException($"Flushing a {nameof(ReadableStream)} is not supported as its underlying data source is a stream."); 25 | } 26 | 27 | public override int Read(byte[] buffer, int offset, int count) 28 | { 29 | throw new InvalidOperationException($"You can't invoke synchronous Stream methods on {nameof(ReadableStream)} because the underlying JS method is asynchronous."); 30 | } 31 | 32 | public override long Seek(long offset, SeekOrigin origin) 33 | { 34 | throw new InvalidOperationException($"Seeking in a {nameof(ReadableStream)} is not supported as its underlying data source is a stream."); 35 | } 36 | 37 | public override void SetLength(long value) 38 | { 39 | throw new InvalidOperationException($"Changing the length of {nameof(ReadableStream)} is not supported as it is meant for reading."); 40 | } 41 | 42 | public override void Write(byte[] buffer, int offset, int count) 43 | { 44 | throw new InvalidOperationException($"Writing to {nameof(ReadableStream)} is not supported as it is meant for reading."); 45 | } 46 | 47 | public override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) 48 | { 49 | reader ??= await GetDefaultReaderAsync(); 50 | ReadableStreamReadResult read = await reader.ReadAsync(); 51 | if (!await read.GetDoneAsync()) 52 | { 53 | IJSObjectReference jSValue = await read.GetValueAsync(); 54 | IJSObjectReference helper = await helperTask.Value; 55 | int length = await helper.InvokeAsync("getAttribute", jSValue, "length"); 56 | (await helper.InvokeAsync("byteArray", jSValue)).CopyTo(buffer); 57 | return length; 58 | } 59 | await reader.ReleaseLockAsync(); 60 | reader = null; 61 | return 0; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/ReadableStream/ReadableStreamBYOBReader.InProcess.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class ReadableStreamBYOBReaderInProcess : ReadableStreamBYOBReader, IJSInProcessCreatable 10 | { 11 | /// 12 | /// An in-process helper module instance from the Blazor.Streams library. 13 | /// 14 | protected readonly IJSInProcessObjectReference inProcessHelper; 15 | 16 | /// 17 | public new IJSInProcessObjectReference JSReference { get; } 18 | 19 | /// 20 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference) 21 | { 22 | return await CreateAsync(jSRuntime, jSReference, new()); 23 | } 24 | 25 | /// 26 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference, CreationOptions options) 27 | { 28 | IJSInProcessObjectReference inProcesshelper = await jSRuntime.GetInProcessHelperAsync(); 29 | return new ReadableStreamBYOBReaderInProcess(jSRuntime, inProcesshelper, jSReference, options); 30 | } 31 | 32 | /// 33 | public static new async Task CreateAsync(IJSRuntime jSRuntime, ReadableStream stream) 34 | { 35 | IJSInProcessObjectReference inProcesshelper = await jSRuntime.GetInProcessHelperAsync(); 36 | IJSInProcessObjectReference jSInstance = inProcesshelper.Invoke("constructReadableStreamBYOBReader", stream.JSReference); 37 | return new ReadableStreamBYOBReaderInProcess(jSRuntime, inProcesshelper, jSInstance, new() { DisposesJSReference = true }); 38 | } 39 | 40 | /// 41 | internal ReadableStreamBYOBReaderInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference inProcessHelper, IJSInProcessObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) 42 | { 43 | this.inProcessHelper = inProcessHelper; 44 | JSReference = jSReference; 45 | } 46 | 47 | /// 48 | /// Reads a chunk of a stream. 49 | /// 50 | /// The that is used as a buffer 51 | /// The next chunk of the underlying . 52 | public new async Task ReadAsync(IArrayBufferView view) 53 | { 54 | IJSInProcessObjectReference jSInstance = await JSReference.InvokeAsync("read", view.JSReference); 55 | return new ReadableStreamReadResultInProcess(JSRuntime, inProcessHelper, jSInstance, new() { DisposesJSReference = true }); 56 | } 57 | 58 | /// 59 | /// Reads a chunk of a stream. 60 | /// 61 | /// The that is used as a buffer 62 | /// The options for how the chunk is to be read. 63 | /// The next chunk of the underlying . 64 | public new async Task ReadAsync(IArrayBufferView view, ReadableStreamBYOBReaderReadOptions? options = null) 65 | { 66 | IJSInProcessObjectReference jSInstance = await JSReference.InvokeAsync("read", view.JSReference, options); 67 | return new ReadableStreamReadResultInProcess(JSRuntime, inProcessHelper, jSInstance, new() { DisposesJSReference = true }); 68 | } 69 | 70 | /// 71 | /// Sets the internal reader slot to undefined. 72 | /// 73 | /// 74 | public void ReleaseLock() 75 | { 76 | JSReference.InvokeVoid("releaseLock"); 77 | } 78 | 79 | /// 80 | /// Gets a JS reference to the closed attribute. 81 | /// 82 | /// 83 | public IJSObjectReference Closed => inProcessHelper.Invoke("getAttribute", JSReference, "closed"); 84 | } 85 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/ReadableStream/ReadableStreamBYOBReader.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class ReadableStreamBYOBReader : ReadableStreamReader, IJSCreatable 10 | { 11 | /// 12 | /// Constructs a wrapper instance for a given JS Instance of a . 13 | /// 14 | /// An instance. 15 | /// A JS reference to an existing . 16 | /// A wrapper instance for a . 17 | [Obsolete("This will be removed in the next major release as all creator methods should be asynchronous for uniformity. Use CreateAsync instead.")] 18 | public static ReadableStreamBYOBReader Create(IJSRuntime jSRuntime, IJSObjectReference jSReference) 19 | { 20 | return new ReadableStreamBYOBReader(jSRuntime, jSReference, new()); 21 | } 22 | 23 | /// 24 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference) 25 | { 26 | return await CreateAsync(jSRuntime, jSReference, new()); 27 | } 28 | 29 | /// 30 | public static Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) 31 | { 32 | return Task.FromResult(new ReadableStreamBYOBReader(jSRuntime, jSReference, options)); 33 | } 34 | 35 | /// 36 | /// Constructs a from some . 37 | /// 38 | /// An IJSRuntime instance. 39 | /// A wrapper instance. 40 | /// 41 | public static async Task CreateAsync(IJSRuntime jSRuntime, ReadableStream stream) 42 | { 43 | IJSObjectReference helper = await jSRuntime.GetHelperAsync(); 44 | IJSObjectReference jSInstance = await helper.InvokeAsync("constructReadableStreamBYOBReader", stream.JSReference); 45 | return new ReadableStreamBYOBReader(jSRuntime, jSInstance, new() { DisposesJSReference = true }); 46 | } 47 | 48 | /// 49 | protected internal ReadableStreamBYOBReader(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) { } 50 | 51 | /// 52 | /// Reads a chunk of a stream. 53 | /// 54 | /// The that acts as a buffer. 55 | /// The next chunk of the underlying . 56 | public async Task ReadAsync(IArrayBufferView view) 57 | { 58 | IJSObjectReference jSInstance = await JSReference.InvokeAsync("read", view.JSReference); 59 | return new ReadableStreamReadResult(JSRuntime, jSInstance, new() { DisposesJSReference = true }); 60 | } 61 | 62 | /// 63 | /// Reads a chunk of a stream. 64 | /// 65 | /// The that acts as a buffer. 66 | /// The options for how the chunk is to be read. 67 | /// The next chunk of the underlying . 68 | public async Task ReadAsync(IArrayBufferView view, ReadableStreamBYOBReaderReadOptions? options = null) 69 | { 70 | IJSObjectReference jSInstance = await JSReference.InvokeAsync("read", view.JSReference, options); 71 | return new ReadableStreamReadResult(JSRuntime, jSInstance, new() { DisposesJSReference = true }); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/ReadableStream/ReadableStreamBYOBRequest.InProcess.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class ReadableStreamBYOBRequestInProcess : ReadableStreamBYOBRequest, IJSInProcessCreatable 10 | { 11 | /// 12 | /// An in-process helper module instance from the Blazor.Streams library. 13 | /// 14 | protected readonly IJSInProcessObjectReference inProcessHelper; 15 | 16 | /// 17 | public new IJSInProcessObjectReference JSReference { get; } 18 | 19 | /// 20 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference) 21 | { 22 | return await CreateAsync(jSRuntime, jSReference, new()); 23 | } 24 | 25 | /// 26 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference, CreationOptions options) 27 | { 28 | IJSInProcessObjectReference inProcesshelper = await jSRuntime.GetInProcessHelperAsync(); 29 | return new ReadableStreamBYOBRequestInProcess(jSRuntime, inProcesshelper, jSReference, options); 30 | } 31 | 32 | /// 33 | protected internal ReadableStreamBYOBRequestInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference inProcessHelper, IJSInProcessObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) 34 | { 35 | this.inProcessHelper = inProcessHelper; 36 | JSReference = jSReference; 37 | } 38 | 39 | /// 40 | [Obsolete("This will be removed in the next major release. We are unable to get a rich wrapper for an Array buffer synchronously so we shouldn't use this.")] 41 | public ArrayBufferView? View 42 | { 43 | get 44 | { 45 | IJSObjectReference? jSInstance = inProcessHelper.Invoke("getAttribute", JSReference, "view"); 46 | if (jSInstance is null) 47 | { 48 | return null; 49 | } 50 | return new ArrayBufferView(jSInstance); 51 | } 52 | } 53 | 54 | /// 55 | public new async Task GetViewAsync() 56 | { 57 | ValueReferenceInProcess viewAttribute = await ValueReferenceInProcess.CreateAsync(JSRuntime, JSReference, "view"); 58 | 59 | viewAttribute.ValueMapper = new() 60 | { 61 | ["float32array"] = async () => await viewAttribute.GetCreatableAsync(), 62 | ["uint8array"] = async () => await viewAttribute.GetCreatableAsync(), 63 | ["uint16array"] = async () => await viewAttribute.GetCreatableAsync(), 64 | ["uint32array"] = async () => await viewAttribute.GetCreatableAsync() 65 | }; 66 | 67 | object? value = await viewAttribute.GetValueAsync(); 68 | 69 | if (value is not IArrayBufferView { } arrayBufferView) 70 | { 71 | string typeName = await viewAttribute.GetTypeNameAsync(); 72 | throw new NotSupportedException($"The type of view '{typeName}' is not supported. If you need to use this you can request support for it in the Blazor.WebIDL library."); 73 | } 74 | 75 | return arrayBufferView; 76 | } 77 | 78 | /// 79 | /// Should be called after having written to the the view. 80 | /// 81 | /// The number of bytes that had been written to the view. 82 | /// 83 | public void Respond(ulong bytesWritten) 84 | { 85 | JSReference.InvokeVoid("respond", bytesWritten); 86 | } 87 | 88 | /// 89 | /// Indicates that there was supplied a new as the source for the write. 90 | /// 91 | /// A new view. The constraints for what this can be are extensive, so look into the documentation if you need this. 92 | public void RespondWithNewView(IArrayBufferView view) 93 | { 94 | JSReference.InvokeVoid("respondWithNewView", view.JSReference); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/ReadableStream/ReadableStreamBYOBRequest.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class ReadableStreamBYOBRequest : BaseJSWrapper, IJSCreatable 10 | { 11 | /// 12 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference) 13 | { 14 | return await CreateAsync(jSRuntime, jSReference, new()); 15 | } 16 | 17 | /// 18 | public static Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) 19 | { 20 | return Task.FromResult(new ReadableStreamBYOBRequest(jSRuntime, jSReference, options)); 21 | } 22 | 23 | /// 24 | protected internal ReadableStreamBYOBRequest(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) { } 25 | 26 | /// 27 | /// The view that should be written to. Is null if it has been responded already. 28 | /// 29 | /// A new concrete implementation of an . 30 | public async Task GetViewAsync() 31 | { 32 | ValueReference viewAttribute = new(JSRuntime, JSReference, "view"); 33 | 34 | viewAttribute.ValueMapper = new() 35 | { 36 | ["float32array"] = async () => await viewAttribute.GetCreatableAsync(), 37 | ["uint8array"] = async () => await viewAttribute.GetCreatableAsync(), 38 | ["uint16array"] = async () => await viewAttribute.GetCreatableAsync(), 39 | ["uint32array"] = async () => await viewAttribute.GetCreatableAsync() 40 | }; 41 | 42 | object? value = await viewAttribute.GetValueAsync(); 43 | 44 | if (value is not IArrayBufferView { } arrayBufferView) 45 | { 46 | string typeName = await viewAttribute.GetTypeNameAsync(); 47 | throw new NotSupportedException($"The type of view '{typeName}' is not supported. If you need to use this you can request support for it in the Blazor.WebIDL library."); 48 | } 49 | 50 | return arrayBufferView; 51 | } 52 | 53 | /// 54 | /// Should be called after having written to the the view. 55 | /// 56 | /// The number of bytes that had been written to the view. 57 | /// 58 | public async Task RespondAsync(ulong bytesWritten) 59 | { 60 | await JSReference.InvokeVoidAsync("respond", bytesWritten); 61 | } 62 | 63 | /// 64 | /// Indicates that there was supplied a new as the source for the write. 65 | /// 66 | /// A new view. The constraints for what this can be are extensive, so look into the documentation if you need this. 67 | public async Task RespondWithNewViewAsync(IArrayBufferView view) 68 | { 69 | await JSReference.InvokeVoidAsync("respondWithNewView", view.JSReference); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/ReadableStream/ReadableStreamController.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public abstract class ReadableStreamController : BaseJSWrapper 10 | { 11 | /// 12 | protected ReadableStreamController(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) { } 13 | 14 | /// 15 | /// The desired size to fill the controlled stream's internal queue. 16 | /// 17 | /// Negative values means that the queue is overfull. 18 | public async Task GetDesiredSizeAsync() 19 | { 20 | IJSObjectReference helper = await helperTask.Value; 21 | return await helper.InvokeAsync("getAttribute", JSReference, "desiredSize"); 22 | } 23 | 24 | /// 25 | /// Closes the controlled stream once all previously enqueued chunks have been read. 26 | /// 27 | /// 28 | public async Task CloseAsync() 29 | { 30 | await JSReference.InvokeVoidAsync("close"); 31 | } 32 | 33 | /// 34 | /// Errors the controlled stream so that all future interactions will fail. 35 | /// 36 | /// 37 | public async Task ErrorAsync() 38 | { 39 | await JSReference.InvokeVoidAsync("error"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/ReadableStream/ReadableStreamDefaultController.InProcess.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class ReadableStreamDefaultControllerInProcess : ReadableStreamDefaultController, IJSInProcessCreatable 10 | { 11 | /// 12 | /// An in-process helper module instance from the Blazor.Streams library. 13 | /// 14 | protected readonly IJSInProcessObjectReference inProcessHelper; 15 | 16 | /// 17 | public new IJSInProcessObjectReference JSReference { get; } 18 | 19 | /// 20 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference) 21 | { 22 | return await CreateAsync(jSRuntime, jSReference, new()); 23 | } 24 | 25 | /// 26 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference, CreationOptions options) 27 | { 28 | IJSInProcessObjectReference inProcesshelper = await jSRuntime.GetInProcessHelperAsync(); 29 | return new ReadableStreamDefaultControllerInProcess(jSRuntime, inProcesshelper, jSReference, options); 30 | } 31 | 32 | /// 33 | internal ReadableStreamDefaultControllerInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference inProcessHelper, IJSInProcessObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) 34 | { 35 | this.inProcessHelper = inProcessHelper; 36 | JSReference = jSReference; 37 | } 38 | 39 | /// 40 | /// The desired size to fill the controlled stream's internal queue. 41 | /// 42 | /// Negative values means that the queue is overfull. 43 | public double? DesiredSize => inProcessHelper.Invoke("getAttribute", JSReference, "desiredSize"); 44 | 45 | /// 46 | /// Closes the controlled stream once all previously enqueued chunks have been read. 47 | /// 48 | /// 49 | public void Close() 50 | { 51 | JSReference.InvokeVoid("close"); 52 | } 53 | 54 | /// 55 | /// Enqueues the chunk in the controlled stream. 56 | /// 57 | /// A JS reference to a chunk. 58 | /// 59 | public void Enqueue(IJSObjectReference chunk) 60 | { 61 | JSReference.InvokeVoid("enqueue", chunk); 62 | } 63 | 64 | /// 65 | /// Errors the controlled stream so that all future interactions will fail. 66 | /// 67 | /// 68 | public void Error() 69 | { 70 | JSReference.InvokeVoid("error"); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/ReadableStream/ReadableStreamDefaultController.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class ReadableStreamDefaultController : ReadableStreamController, IJSCreatable 10 | { 11 | /// 12 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference) 13 | { 14 | return await CreateAsync(jSRuntime, jSReference, new()); 15 | } 16 | 17 | /// 18 | public static Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) 19 | { 20 | return Task.FromResult(new ReadableStreamDefaultController(jSRuntime, jSReference, options)); 21 | } 22 | 23 | /// 24 | protected internal ReadableStreamDefaultController(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) { } 25 | 26 | /// 27 | /// Enqueues the chunk in the controlled stream. 28 | /// 29 | /// A JS reference to a chunk. 30 | /// 31 | public async Task EnqueueAsync(IJSObjectReference chunk) 32 | { 33 | await JSReference.InvokeVoidAsync("enqueue", chunk); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/ReadableStream/ReadableStreamDefaultReader.InProcess.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class ReadableStreamDefaultReaderInProcess : ReadableStreamDefaultReader, IJSInProcessCreatable 10 | { 11 | /// 12 | /// An in-process helper module instance from the Blazor.Streams library. 13 | /// 14 | protected readonly IJSInProcessObjectReference inProcessHelper; 15 | 16 | /// 17 | public new IJSInProcessObjectReference JSReference { get; } 18 | 19 | /// 20 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference) 21 | { 22 | return await CreateAsync(jSRuntime, jSReference, new()); 23 | } 24 | 25 | /// 26 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference, CreationOptions options) 27 | { 28 | IJSInProcessObjectReference inProcesshelper = await jSRuntime.GetInProcessHelperAsync(); 29 | return new ReadableStreamDefaultReaderInProcess(jSRuntime, inProcesshelper, jSReference, options); 30 | } 31 | 32 | /// 33 | /// Constructs a from some . 34 | /// 35 | /// An IJSRuntime instance. 36 | /// A wrapper instance. 37 | /// A wrapper instance for a . 38 | public static new async Task CreateAsync(IJSRuntime jSRuntime, ReadableStream stream) 39 | { 40 | IJSInProcessObjectReference inProcesshelper = await jSRuntime.GetInProcessHelperAsync(); 41 | IJSInProcessObjectReference jSInstance = inProcesshelper.Invoke("constructReadableStreamDefaultReader", stream.JSReference); 42 | return new ReadableStreamDefaultReaderInProcess(jSRuntime, inProcesshelper, jSInstance, new() { DisposesJSReference = true }); 43 | } 44 | 45 | /// 46 | protected internal ReadableStreamDefaultReaderInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference inProcessHelper, IJSInProcessObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) 47 | { 48 | this.inProcessHelper = inProcessHelper; 49 | JSReference = jSReference; 50 | } 51 | 52 | /// 53 | /// Reads a chunk of a stream. 54 | /// 55 | /// The next chunk of the underlying . 56 | public new async Task ReadAsync() 57 | { 58 | IJSInProcessObjectReference jSInstance = await JSReference.InvokeAsync("read"); 59 | return new ReadableStreamReadResultInProcess(JSRuntime, inProcessHelper, jSInstance, new() { DisposesJSReference = true }); 60 | } 61 | 62 | /// 63 | /// Sets the internal reader slot to undefined. 64 | /// 65 | /// 66 | public void ReleaseLock() 67 | { 68 | JSReference.InvokeVoid("releaseLock"); 69 | } 70 | 71 | /// 72 | /// Gets a JS reference to the closed attribute. 73 | /// 74 | /// 75 | public IJSObjectReference Closed => inProcessHelper.Invoke("getAttribute", JSReference, "closed"); 76 | } 77 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/ReadableStream/ReadableStreamDefaultReader.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace KristofferStrube.Blazor.Streams; 6 | 7 | /// 8 | /// Streams browser specs 9 | /// 10 | public class ReadableStreamDefaultReader : ReadableStreamReader, IAsyncEnumerable, IJSCreatable 11 | { 12 | /// 13 | /// Constructs a wrapper instance for a given JS Instance of a . 14 | /// 15 | /// An instance. 16 | /// A JS reference to an existing . 17 | /// A wrapper instance for a . 18 | [Obsolete("This will be removed in the next major release as all creator methods should be asynchronous for uniformity. Use CreateAsync instead.")] 19 | public static ReadableStreamDefaultReader Create(IJSRuntime jSRuntime, IJSObjectReference jSReference) 20 | { 21 | return new ReadableStreamDefaultReader(jSRuntime, jSReference, new()); 22 | } 23 | 24 | /// 25 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference) 26 | { 27 | return await CreateAsync(jSRuntime, jSReference, new()); 28 | } 29 | 30 | /// 31 | public static Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) 32 | { 33 | return Task.FromResult(new ReadableStreamDefaultReader(jSRuntime, jSReference, options)); 34 | } 35 | 36 | /// 37 | /// Constructs a from some . 38 | /// 39 | /// An IJSRuntime instance. 40 | /// A wrapper instance. 41 | /// A wrapper instance for a . 42 | public static async Task CreateAsync(IJSRuntime jSRuntime, ReadableStream stream) 43 | { 44 | IJSObjectReference helper = await jSRuntime.GetHelperAsync(); 45 | IJSObjectReference jSInstance = await helper.InvokeAsync("constructReadableStreamDefaultReader", stream.JSReference); 46 | return new ReadableStreamDefaultReader(jSRuntime, jSInstance, new() { DisposesJSReference = true }); 47 | } 48 | 49 | /// 50 | protected internal ReadableStreamDefaultReader(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) { } 51 | 52 | /// 53 | /// Reads a chunk of a stream. 54 | /// 55 | /// The next chunk of the underlying . 56 | public async Task ReadAsync() 57 | { 58 | IJSObjectReference jSInstance = await JSReference.InvokeAsync("read"); 59 | return new ReadableStreamReadResult(JSRuntime, jSInstance, new() { DisposesJSReference = true }); 60 | } 61 | 62 | public async IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) 63 | { 64 | ReadableStreamReadResult read = await ReadAsync(); 65 | while (!await read.GetDoneAsync() && !cancellationToken.IsCancellationRequested) 66 | { 67 | yield return await read.GetValueAsync(); 68 | read = await ReadAsync(); 69 | } 70 | } 71 | 72 | /// 73 | /// Iterates the reader resulting in each chunk as byte arrays, 74 | /// 75 | /// A cancellation token for breaking the enumeration. 76 | /// 77 | public async IAsyncEnumerable IterateByteArraysAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) 78 | { 79 | IJSObjectReference helper = await helperTask.Value; 80 | ReadableStreamReadResult read = await ReadAsync(); 81 | while (!await read.GetDoneAsync() && !cancellationToken.IsCancellationRequested) 82 | { 83 | IJSObjectReference value = await read.GetValueAsync(); 84 | yield return await helper.InvokeAsync("byteArray", value); 85 | read = await ReadAsync(); 86 | } 87 | } 88 | 89 | /// 90 | /// Iterates the reader resulting in each chunk as a string with some optional encoding. The defualt encodig is , 91 | /// 92 | /// A cancellation token for breaking the enumeration. 93 | /// A cancellation token for breaking the enumeration. 94 | /// 95 | public async IAsyncEnumerable IterateStringsAsync(System.Text.Encoding? encoding = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) 96 | { 97 | encoding ??= System.Text.Encoding.ASCII; 98 | IJSObjectReference helper = await helperTask.Value; 99 | ReadableStreamReadResult read = await ReadAsync(); 100 | while (!await read.GetDoneAsync() && !cancellationToken.IsCancellationRequested) 101 | { 102 | IJSObjectReference value = await read.GetValueAsync(); 103 | byte[] byteArray = await helper.InvokeAsync("byteArray", cancellationToken, value); 104 | yield return encoding.GetString(byteArray); 105 | read = await ReadAsync(); 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/ReadableStream/ReadableStreamReadResult.InProcess.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class ReadableStreamReadResultInProcess : ReadableStreamReadResult 10 | { 11 | /// 12 | /// An in-process helper module instance from the Blazor.Streams library. 13 | /// 14 | protected readonly IJSInProcessObjectReference inProcessHelper; 15 | 16 | /// 17 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference) 18 | { 19 | return await CreateAsync(jSRuntime, jSReference, new()); 20 | } 21 | 22 | /// 23 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference, CreationOptions options) 24 | { 25 | IJSInProcessObjectReference inProcesshelper = await jSRuntime.GetInProcessHelperAsync(); 26 | return new ReadableStreamReadResultInProcess(jSRuntime, inProcesshelper, jSReference, options); 27 | } 28 | 29 | /// 30 | internal ReadableStreamReadResultInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference inProcessHelper, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) 31 | { 32 | this.inProcessHelper = inProcessHelper; 33 | } 34 | 35 | /// 36 | /// A JS Reference to a chunk of data. 37 | /// 38 | /// An to a value which can be of any type. 39 | public IJSInProcessObjectReference Value => inProcessHelper.Invoke("getAttribute", JSReference, "value"); 40 | 41 | /// 42 | /// Indicates whether this is the last read which means that will be undefined. 43 | /// 44 | /// if the chunk is the last which contains no else 45 | public bool Done => inProcessHelper.Invoke("getAttribute", JSReference, "done"); 46 | } 47 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/ReadableStream/ReadableStreamReadResult.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class ReadableStreamReadResult : BaseJSWrapper, IJSCreatable 10 | { 11 | /// 12 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference) 13 | { 14 | return await CreateAsync(jSRuntime, jSReference, new()); 15 | } 16 | 17 | /// 18 | public static Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) 19 | { 20 | return Task.FromResult(new ReadableStreamReadResult(jSRuntime, jSReference, options)); 21 | } 22 | 23 | /// 24 | protected internal ReadableStreamReadResult(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) { } 25 | 26 | /// 27 | /// A JS Reference to a chunk of data. 28 | /// 29 | /// A to a value which can be of any type. 30 | public async Task GetValueAsync() 31 | { 32 | IJSObjectReference helper = await helperTask.Value; 33 | return await helper.InvokeAsync("getAttribute", JSReference, "value"); 34 | } 35 | 36 | /// 37 | /// Indicates whether this is the last read which means that will be undefined. 38 | /// 39 | /// if the chunk is the last which contains no else 40 | public async Task GetDoneAsync() 41 | { 42 | IJSObjectReference helper = await helperTask.Value; 43 | return await helper.InvokeAsync("getAttribute", JSReference, "done"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/ReadableStream/ReadableStreamReader.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public abstract class ReadableStreamReader : BaseJSWrapper 10 | { 11 | /// 12 | protected ReadableStreamReader(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) { } 13 | 14 | /// 15 | /// Sets the internal reader slot to undefined. 16 | /// 17 | /// 18 | public async Task ReleaseLockAsync() 19 | { 20 | await JSReference.InvokeVoidAsync("releaseLock"); 21 | } 22 | 23 | /// 24 | /// Gets a JS reference to the closed attribute. 25 | /// 26 | /// 27 | public async Task GetClosedAsync() 28 | { 29 | IJSObjectReference helper = await helperTask.Value; 30 | return await helper.InvokeAsync("getAttribute", JSReference, "closed"); 31 | } 32 | 33 | /// 34 | /// Cancels the underlying stream which is equivalent to 35 | /// 36 | /// 37 | public async Task CancelAsync() 38 | { 39 | await JSReference.InvokeVoidAsync("cancel"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/ReadableWritablePair.InProcess.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class ReadableWritablePairInProcess : ReadableWritablePair, IGenericTransformStreamInProcess, IJSInProcessCreatable 10 | { 11 | /// 12 | /// An in-process helper module instance from the Blazor.Streams library. 13 | /// 14 | protected readonly IJSInProcessObjectReference inProcessHelper; 15 | 16 | /// 17 | public new IJSInProcessObjectReference JSReference { get; } 18 | 19 | /// 20 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference) 21 | { 22 | return await CreateAsync(jSRuntime, jSReference, new()); 23 | } 24 | 25 | /// 26 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference, CreationOptions options) 27 | { 28 | IJSInProcessObjectReference inProcesshelper = await jSRuntime.GetInProcessHelperAsync(); 29 | return new ReadableWritablePairInProcess(jSRuntime, inProcesshelper, jSReference, options); 30 | } 31 | 32 | /// 33 | /// Constructs a wrapper instance using the standard constructor 34 | /// 35 | /// An IJSRuntime instance. 36 | /// The part of the pair. 37 | /// The part of the pair. 38 | /// A wrapper instance for a . 39 | public static new async Task CreateAsync(IJSRuntime jSRuntime, ReadableStream readable, WritableStream writable) 40 | { 41 | IJSInProcessObjectReference inProcesshelper = await jSRuntime.GetInProcessHelperAsync(); 42 | IJSInProcessObjectReference jSInstance = inProcesshelper.Invoke("constructReadableWritablePair", readable, writable); 43 | return new ReadableWritablePairInProcess(jSRuntime, inProcesshelper, jSInstance, new() { DisposesJSReference = true }); 44 | } 45 | 46 | /// 47 | protected ReadableWritablePairInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference inProcessHelper, IJSInProcessObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) 48 | { 49 | this.inProcessHelper = inProcessHelper; 50 | JSReference = jSReference; 51 | } 52 | 53 | [Obsolete("This will be removed in the next major release as all creator methods should be asynchronous for uniformity. Use GetReadableAsync instead.")] 54 | public ReadableStreamInProcess Readable 55 | { 56 | get 57 | { 58 | IJSInProcessObjectReference jSInstance = inProcessHelper.Invoke("getAttribute", JSReference, "readable"); 59 | return new ReadableStreamInProcess(JSRuntime, inProcessHelper, jSInstance, new() { DisposesJSReference = true }); 60 | } 61 | set => inProcessHelper.InvokeVoid("setAttribute", JSReference, "readable", value.JSReference); 62 | } 63 | 64 | [Obsolete("This will be removed in the next major release as all creator methods should be asynchronous for uniformity. Use GetWritableAsync instead.")] 65 | public WritableStreamInProcess Writable 66 | { 67 | get 68 | { 69 | IJSInProcessObjectReference jSInstance = inProcessHelper.Invoke("getAttribute", JSReference, "writable"); 70 | return new WritableStreamInProcess(JSRuntime, inProcessHelper, jSInstance, new() { DisposesJSReference = true }); 71 | } 72 | set => inProcessHelper.InvokeVoid("setAttribute", JSReference, "writable", value.JSReference); 73 | } 74 | 75 | IJSInProcessObjectReference IJSInProcessCreatable.JSReference => throw new NotImplementedException(); 76 | 77 | public new async Task GetReadableAsync() 78 | { 79 | IJSObjectReference helper = await helperTask.Value; 80 | IJSInProcessObjectReference jSInstance = await helper.InvokeAsync("getAttribute", JSReference, "readable"); 81 | return await ReadableStreamInProcess.CreateAsync(JSRuntime, jSInstance); 82 | } 83 | 84 | public new async Task GetWritableAsync() 85 | { 86 | IJSObjectReference helper = await helperTask.Value; 87 | IJSInProcessObjectReference jSInstance = await helper.InvokeAsync("getAttribute", JSReference, "writable"); 88 | return await WritableStreamInProcess.CreateAsync(JSRuntime, jSInstance); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/ReadableWritablePair.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class ReadableWritablePair : BaseJSWrapper, IGenericTransformStream, IJSCreatable 10 | { 11 | /// 12 | /// Constructs a wrapper instance for a given JS Instance of a . 13 | /// 14 | /// An instance. 15 | /// A JS reference to an existing . 16 | /// A wrapper instance for a . 17 | [Obsolete("This will be removed in the next major release as all creator methods should be asynchronous for uniformity. Use CreateAsync instead.")] 18 | public static ReadableWritablePair Create(IJSRuntime jSRuntime, IJSObjectReference jSReference) 19 | { 20 | return new ReadableWritablePair(jSRuntime, jSReference, new()); 21 | } 22 | 23 | /// 24 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference) 25 | { 26 | return await CreateAsync(jSRuntime, jSReference, new()); 27 | } 28 | 29 | /// 30 | public static Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) 31 | { 32 | return Task.FromResult(new ReadableWritablePair(jSRuntime, jSReference, options)); 33 | } 34 | 35 | /// 36 | /// Constructs a wrapper instance using the standard constructor 37 | /// 38 | /// An IJSRuntime instance. 39 | /// The part of the pair. 40 | /// The part of the pair. 41 | /// A wrapper instance for a . 42 | public static async Task CreateAsync(IJSRuntime jSRuntime, ReadableStream readable, WritableStream writable) 43 | { 44 | IJSObjectReference helper = await jSRuntime.GetHelperAsync(); 45 | IJSObjectReference jSInstance = await helper.InvokeAsync("constructReadableWritablePair", readable.JSReference, writable.JSReference); 46 | return new ReadableWritablePair(jSRuntime, jSInstance, new() { DisposesJSReference = true }); 47 | } 48 | 49 | /// 50 | protected ReadableWritablePair(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) { } 51 | 52 | public async Task GetReadableAsync() 53 | { 54 | IJSObjectReference helper = await helperTask.Value; 55 | IJSObjectReference jSInstance = await helper.InvokeAsync("getAttribute", JSReference, "readable"); 56 | return await ReadableStream.CreateAsync(JSRuntime, jSInstance); 57 | } 58 | 59 | public async Task GetWritableAsync() 60 | { 61 | IJSObjectReference helper = await helperTask.Value; 62 | IJSObjectReference jSInstance = await helper.InvokeAsync("getAttribute", JSReference, "writable"); 63 | return await WritableStream.CreateAsync(JSRuntime, jSInstance); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/TransformStream/IGenericTransformStream.InProcess.cs: -------------------------------------------------------------------------------- 1 | namespace KristofferStrube.Blazor.Streams; 2 | 3 | /// 4 | /// Streams browser specs 5 | /// 6 | public interface IGenericTransformStreamInProcess : IGenericTransformStream 7 | { 8 | public new Task GetReadableAsync(); 9 | public new Task GetWritableAsync(); 10 | } 11 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/TransformStream/IGenericTransformStream.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | 3 | namespace KristofferStrube.Blazor.Streams; 4 | 5 | /// 6 | /// Streams browser specs 7 | /// 8 | public interface IGenericTransformStream : IJSWrapper 9 | { 10 | public Task GetReadableAsync(); 11 | public Task GetWritableAsync(); 12 | } 13 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/TransformStream/TransformStreamDefaultController.InProcess.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class TransformStreamDefaultControllerInProcess : TransformStreamDefaultController, IJSInProcessCreatable 10 | { 11 | /// 12 | /// An in-process helper module instance from the Blazor.Streams library. 13 | /// 14 | protected readonly IJSInProcessObjectReference inProcessHelper; 15 | 16 | /// 17 | public new IJSInProcessObjectReference JSReference { get; } 18 | 19 | /// 20 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference) 21 | { 22 | return await CreateAsync(jSRuntime, jSReference, new()); 23 | } 24 | 25 | /// 26 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference, CreationOptions options) 27 | { 28 | IJSInProcessObjectReference inProcesshelper = await jSRuntime.GetInProcessHelperAsync(); 29 | return new TransformStreamDefaultControllerInProcess(jSRuntime, inProcesshelper, jSReference, options); 30 | } 31 | 32 | /// 33 | protected internal TransformStreamDefaultControllerInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference inProcessHelper, IJSInProcessObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) 34 | { 35 | this.inProcessHelper = inProcessHelper; 36 | JSReference = jSReference; 37 | } 38 | 39 | /// 40 | /// The desired size to fill the controlled stream's internal queue. 41 | /// 42 | /// Negative values means that the queue is overfull. 43 | public double? DesiredSize => inProcessHelper.Invoke("getAttribute", JSReference, "desiredSize"); 44 | 45 | /// 46 | /// Enqueues a chunk on the readable side of the transfor stream. 47 | /// 48 | /// A JS reference to a chunk. 49 | /// 50 | public void Enqueue(IJSObjectReference chunk) 51 | { 52 | JSReference.InvokeVoid("enqueue", chunk); 53 | } 54 | 55 | /// 56 | /// Errors both the readable and writable side of the transform stream. 57 | /// 58 | /// 59 | public void Error() 60 | { 61 | JSReference.InvokeVoid("error"); 62 | } 63 | 64 | /// 65 | /// Closes the readable side and errors the writable side. 66 | /// 67 | /// 68 | public void Terminate() 69 | { 70 | JSReference.InvokeVoid("terminate"); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/TransformStream/TransformStreamDefaultController.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class TransformStreamDefaultController : BaseJSWrapper, IJSCreatable 10 | { 11 | /// 12 | /// Constructs a wrapper instance for a given JS Instance of a . 13 | /// 14 | /// An instance. 15 | /// A JS reference to an existing . 16 | /// A wrapper instance for a . 17 | [Obsolete("This will be removed in the next major release as all creator methods should be asynchronous for uniformity. Use CreateAsync instead.")] 18 | public static TransformStreamDefaultController Create(IJSRuntime jSRuntime, IJSObjectReference jSReference) 19 | { 20 | return new TransformStreamDefaultController(jSRuntime, jSReference, new()); 21 | } 22 | 23 | /// 24 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference) 25 | { 26 | return await CreateAsync(jSRuntime, jSReference, new()); 27 | } 28 | 29 | /// 30 | public static Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) 31 | { 32 | return Task.FromResult(new TransformStreamDefaultController(jSRuntime, jSReference, options)); 33 | } 34 | 35 | /// 36 | protected internal TransformStreamDefaultController(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) { } 37 | 38 | /// 39 | /// The desired size to fill the controlled stream's internal queue. 40 | /// 41 | /// Negative values means that the queue is overfull. 42 | public async Task GetDesiredSizeAsync() 43 | { 44 | IJSObjectReference helper = await helperTask.Value; 45 | return await helper.InvokeAsync("getAttribute", JSReference, "desiredSize"); 46 | } 47 | 48 | /// 49 | /// Enqueues a chunk on the readable side of the transfor stream. 50 | /// 51 | /// A JS reference to a chunk. 52 | /// 53 | public async Task EnqueueAsync(IJSObjectReference chunk) 54 | { 55 | await JSReference.InvokeVoidAsync("enqueue", chunk); 56 | } 57 | 58 | /// 59 | /// Errors both the readable and writable side of the transform stream. 60 | /// 61 | /// 62 | public async Task ErrorAsync() 63 | { 64 | await JSReference.InvokeVoidAsync("error"); 65 | } 66 | 67 | /// 68 | /// Closes the readable side and errors the writable side. 69 | /// 70 | /// 71 | public async Task TerminateAsync() 72 | { 73 | await JSReference.InvokeVoidAsync("terminate"); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/WritableStream/WritableStream.InProcess.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class WritableStreamInProcess : WritableStream, IJSInProcessCreatable 10 | { 11 | /// 12 | /// An in-process helper module instance from the Blazor.Streams library. 13 | /// 14 | protected readonly IJSInProcessObjectReference inProcessHelper; 15 | 16 | /// 17 | public new IJSInProcessObjectReference JSReference { get; } 18 | 19 | /// 20 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference) 21 | { 22 | return await CreateAsync(jSRuntime, jSReference, new()); 23 | } 24 | 25 | /// 26 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference, CreationOptions options) 27 | { 28 | IJSInProcessObjectReference inProcesshelper = await jSRuntime.GetInProcessHelperAsync(); 29 | return new WritableStreamInProcess(jSRuntime, inProcesshelper, jSReference, options); 30 | } 31 | 32 | /// 33 | /// Constructs a wrapper instance using the standard constructor 34 | /// 35 | /// An IJSRuntime instance. 36 | /// An that which implements the Start, Write, Close, and/or Abort methods. 37 | /// A queing strategy that specifies the chunk size and a high water mark. 38 | /// A wrapper instance for a . 39 | public static new async Task CreateAsync(IJSRuntime jSRuntime, UnderlyingSink? underlyingSink = null, QueuingStrategy? strategy = null) 40 | { 41 | return await CreatePrivateAsync(jSRuntime, underlyingSink, strategy); 42 | } 43 | 44 | /// 45 | /// Constructs a wrapper instance using the standard constructor 46 | /// 47 | /// An IJSRuntime instance. 48 | /// An that which implements the Start, Write, Close, and/or Abort methods. 49 | /// A queing strategy that specifies the chunk size and a high water mark. 50 | /// A wrapper instance for a . 51 | public static new async Task CreateAsync(IJSRuntime jSRuntime, UnderlyingSink? underlyingSink = null, ByteLengthQueuingStrategy? strategy = null) 52 | { 53 | return await CreatePrivateAsync(jSRuntime, underlyingSink, strategy?.JSReference); 54 | } 55 | 56 | /// 57 | /// Constructs a wrapper instance using the standard constructor 58 | /// 59 | /// An IJSRuntime instance. 60 | /// An that which implements the Start, Write, Close, and/or Abort methods. 61 | /// A queing strategy that specifies the chunk size and a high water mark. 62 | /// A wrapper instance for a . 63 | public static new async Task CreateAsync(IJSRuntime jSRuntime, UnderlyingSink? underlyingSink = null, CountQueuingStrategy? strategy = null) 64 | { 65 | return await CreatePrivateAsync(jSRuntime, underlyingSink, strategy?.JSReference); 66 | } 67 | 68 | private static async Task CreatePrivateAsync(IJSRuntime jSRuntime, object? underlyingSink = null, object? strategy = null) 69 | { 70 | IJSInProcessObjectReference inProcessHelper = await jSRuntime.GetInProcessHelperAsync(); 71 | IJSInProcessObjectReference jSInstance = await inProcessHelper.InvokeAsync("constructWritableStream", underlyingSink, strategy); 72 | return new WritableStreamInProcess(jSRuntime, inProcessHelper, jSInstance, new() { DisposesJSReference = true }); 73 | } 74 | 75 | /// 76 | protected internal WritableStreamInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference inProcessHelper, IJSInProcessObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) 77 | { 78 | this.inProcessHelper = inProcessHelper; 79 | JSReference = jSReference; 80 | } 81 | 82 | /// 83 | /// Indicates whether the stream already has a writer. 84 | /// 85 | /// if the internal writer slot is undefined else return 86 | public bool Locked => inProcessHelper.Invoke("getAttribute", JSReference, "locked"); 87 | 88 | 89 | /// 90 | /// Creates a new that it assigns to the internal writer slot and returns that. 91 | /// 92 | /// A new . 93 | public WritableStreamDefaultWriterInProcess GetWriter() 94 | { 95 | IJSInProcessObjectReference jSInstance = JSReference.Invoke("getWriter"); 96 | return new WritableStreamDefaultWriterInProcess(JSRuntime, inProcessHelper, jSInstance, new() { DisposesJSReference = true }); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/WritableStream/WritableStream.Stream.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | 3 | namespace KristofferStrube.Blazor.Streams; 4 | 5 | public partial class WritableStream 6 | { 7 | private WritableStreamDefaultWriter? writer; 8 | 9 | public override bool CanRead => false; 10 | 11 | public override bool CanSeek => false; 12 | 13 | public override bool CanWrite => true; 14 | 15 | /// 16 | /// We can't check the length of a . 17 | /// 18 | public override long Length => 0; 19 | 20 | public override long Position { get; set; } 21 | 22 | public override void Flush() 23 | { 24 | throw new InvalidOperationException($"You can't invoke synchronous Stream methods on {nameof(WritableStream)} because the underlying JS method is asynchronous."); 25 | } 26 | 27 | public override int Read(byte[] buffer, int offset, int count) 28 | { 29 | throw new InvalidOperationException($"Reading a {nameof(WritableStream)} is not supported as it is meant for writing."); 30 | } 31 | 32 | public override long Seek(long offset, SeekOrigin origin) 33 | { 34 | throw new InvalidOperationException($"Seeking in a {nameof(WritableStream)} is not supported as its underlying data source is a stream."); 35 | } 36 | 37 | public override void SetLength(long value) 38 | { 39 | throw new InvalidOperationException($"Changing the length of {nameof(WritableStream)} is not supported as its underlying data source is a stream."); 40 | } 41 | 42 | public override void Write(byte[] buffer, int offset, int count) 43 | { 44 | throw new InvalidOperationException($"You can't invoke synchronous Stream methods on {nameof(WritableStream)} because the underlying JS method is asynchronous."); 45 | } 46 | 47 | public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) 48 | { 49 | await WriteAsync(buffer, cancellationToken); 50 | } 51 | 52 | public override async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) 53 | { 54 | writer ??= await GetWriterAsync(); 55 | IJSObjectReference helper = await helperTask.Value; 56 | await writer.WriteAsync(await helper.InvokeAsync("valueOf", buffer.ToArray())); 57 | } 58 | 59 | public override async Task FlushAsync(CancellationToken cancellationToken) 60 | { 61 | if (writer is null) 62 | { 63 | return; 64 | } 65 | 66 | await writer.CloseAsync(); 67 | writer = null; 68 | } 69 | 70 | public override async ValueTask DisposeAsync() 71 | { 72 | await base.DisposeAsync(); 73 | await CloseAsync(); 74 | GC.SuppressFinalize(this); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/WritableStream/WritableStream.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public partial class WritableStream : BaseJSStreamableWrapper, IJSCreatable 10 | { 11 | /// 12 | /// Constructs a wrapper instance for a given JS Instance of a . 13 | /// 14 | /// An instance. 15 | /// A JS reference to an existing . 16 | /// A wrapper instance for a . 17 | [Obsolete("This will be removed in the next major release as all creator methods should be asynchronous for uniformity. Use CreateAsync instead.")] 18 | public static WritableStream Create(IJSRuntime jSRuntime, IJSObjectReference jSReference) 19 | { 20 | return new WritableStream(jSRuntime, jSReference, new()); 21 | } 22 | 23 | /// 24 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference) 25 | { 26 | return await CreateAsync(jSRuntime, jSReference, new()); 27 | } 28 | 29 | /// 30 | public static Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) 31 | { 32 | return Task.FromResult(new WritableStream(jSRuntime, jSReference, options)); 33 | } 34 | 35 | /// 36 | /// Constructs a wrapper instance using the standard constructor 37 | /// 38 | /// An IJSRuntime instance. 39 | /// An that which implements the Start, Write, Close, and/or Abort methods. 40 | /// A queing strategy that specifies the chunk size and a high water mark. 41 | /// A wrapper instance for a . 42 | public static async Task CreateAsync(IJSRuntime jSRuntime, UnderlyingSink? underlyingSink = null, QueuingStrategy? strategy = null) 43 | { 44 | return await CreatePrivateAsync(jSRuntime, underlyingSink, strategy); 45 | } 46 | 47 | /// 48 | /// Constructs a wrapper instance using the standard constructor 49 | /// 50 | /// An IJSRuntime instance. 51 | /// An that which implements the Start, Write, Close, and/or Abort methods. 52 | /// A queing strategy that specifies the chunk size and a high water mark. 53 | /// A wrapper instance for a . 54 | public static async Task CreateAsync(IJSRuntime jSRuntime, UnderlyingSink? underlyingSink = null, ByteLengthQueuingStrategy? strategy = null) 55 | { 56 | return await CreatePrivateAsync(jSRuntime, underlyingSink, strategy?.JSReference); 57 | } 58 | 59 | /// 60 | /// Constructs a wrapper instance using the standard constructor 61 | /// 62 | /// An IJSRuntime instance. 63 | /// An that which implements the Start, Write, Close, and/or Abort methods. 64 | /// A queing strategy that specifies the chunk size and a high water mark. 65 | /// A wrapper instance for a . 66 | public static async Task CreateAsync(IJSRuntime jSRuntime, UnderlyingSink? underlyingSink = null, CountQueuingStrategy? strategy = null) 67 | { 68 | return await CreatePrivateAsync(jSRuntime, underlyingSink, strategy?.JSReference); 69 | } 70 | 71 | private static async Task CreatePrivateAsync(IJSRuntime jSRuntime, object? underlyingSink = null, object? strategy = null) 72 | { 73 | IJSObjectReference helper = await jSRuntime.GetHelperAsync(); 74 | IJSObjectReference jSInstance = await helper.InvokeAsync("constructWritableStream", underlyingSink, strategy); 75 | return new WritableStream(jSRuntime, jSInstance, new() { DisposesJSReference = true }); 76 | } 77 | 78 | /// 79 | protected WritableStream(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) { } 80 | 81 | /// 82 | /// Indicates whether the stream already has a writer. 83 | /// 84 | /// if the internal writer slot is undefined else return 85 | public async Task GetLockedAsync() 86 | { 87 | IJSObjectReference helper = await helperTask.Value; 88 | return await helper.InvokeAsync("getAttribute", JSReference, "locked"); 89 | } 90 | 91 | /// 92 | /// Aborts the stream if it is not locked. 93 | /// 94 | /// 95 | public async Task AbortAsync() 96 | { 97 | await JSReference.InvokeVoidAsync("abort"); 98 | } 99 | 100 | /// 101 | /// Closes the stream if it is not locked. 102 | /// 103 | /// 104 | public async Task CloseAsync() 105 | { 106 | await JSReference.InvokeVoidAsync("close"); 107 | } 108 | 109 | /// 110 | /// Creates a new that it assigns to the internal writer slot and returns that. 111 | /// 112 | /// A new . 113 | public async Task GetWriterAsync() 114 | { 115 | IJSObjectReference jSInstance = await JSReference.InvokeAsync("getWriter"); 116 | return await WritableStreamDefaultWriter.CreateAsync(JSRuntime, jSInstance, new() { DisposesJSReference = true }); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/WritableStream/WritableStreamDefaultController.InProcess.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class WritableStreamDefaultControllerInProcess : WritableStreamDefaultController 10 | { 11 | /// 12 | /// An in-process helper module instance from the Blazor.Streams library. 13 | /// 14 | protected readonly IJSInProcessObjectReference inProcessHelper; 15 | 16 | /// 17 | public new IJSInProcessObjectReference JSReference { get; } 18 | 19 | /// 20 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference) 21 | { 22 | return await CreateAsync(jSRuntime, jSReference, new()); 23 | } 24 | 25 | /// 26 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference, CreationOptions options) 27 | { 28 | IJSInProcessObjectReference inProcesshelper = await jSRuntime.GetInProcessHelperAsync(); 29 | return new WritableStreamDefaultControllerInProcess(jSRuntime, inProcesshelper, jSReference, options); 30 | } 31 | 32 | /// 33 | protected internal WritableStreamDefaultControllerInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference inProcessHelper, IJSInProcessObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) 34 | { 35 | this.inProcessHelper = inProcessHelper; 36 | JSReference = jSReference; 37 | } 38 | 39 | /// 40 | /// Errors the controlled stream so that all future interactions will fail. 41 | /// 42 | /// 43 | public void Error() 44 | { 45 | inProcessHelper.InvokeVoid("error"); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/WritableStream/WritableStreamDefaultController.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class WritableStreamDefaultController : BaseJSWrapper, IJSCreatable 10 | { 11 | /// 12 | /// Constructs a wrapper instance for a given JS Instance of a . 13 | /// 14 | /// An instance. 15 | /// A JS reference to an existing . 16 | /// A wrapper instance for a . 17 | [Obsolete("This will be removed in the next major release as all creator methods should be asynchronous for uniformity. Use CreateAsync instead.")] 18 | public static WritableStreamDefaultController Create(IJSRuntime jSRuntime, IJSObjectReference jSReference) 19 | { 20 | return new WritableStreamDefaultController(jSRuntime, jSReference, new()); 21 | } 22 | 23 | /// 24 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference) 25 | { 26 | return await CreateAsync(jSRuntime, jSReference, new()); 27 | } 28 | 29 | /// 30 | public static Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) 31 | { 32 | return Task.FromResult(new WritableStreamDefaultController(jSRuntime, jSReference, options)); 33 | } 34 | 35 | /// 36 | protected internal WritableStreamDefaultController(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) { } 37 | 38 | /// 39 | /// Errors the controlled stream so that all future interactions will fail. 40 | /// 41 | /// 42 | public async Task ErrorAsync() 43 | { 44 | await JSReference.InvokeVoidAsync("error"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/WritableStream/WritableStreamDefaultWriter.InProcess.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class WritableStreamDefaultWriterInProcess : WritableStreamDefaultWriter, IJSInProcessCreatable 10 | { 11 | /// 12 | /// An in-process helper module instance from the Blazor.Streams library. 13 | /// 14 | protected readonly IJSInProcessObjectReference inProcessHelper; 15 | 16 | /// 17 | public new IJSInProcessObjectReference JSReference { get; } 18 | 19 | /// 20 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference) 21 | { 22 | return await CreateAsync(jSRuntime, jSReference, new()); 23 | } 24 | 25 | /// 26 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference, CreationOptions options) 27 | { 28 | IJSInProcessObjectReference inProcesshelper = await jSRuntime.GetInProcessHelperAsync(); 29 | return new WritableStreamDefaultWriterInProcess(jSRuntime, inProcesshelper, jSReference, options); 30 | } 31 | 32 | /// 33 | /// Constructs a from some . 34 | /// 35 | /// An IJSRuntime instance. 36 | /// A wrapper instance. 37 | /// A wrapper instance for a . 38 | public static new async Task CreateAsync(IJSRuntime jSRuntime, WritableStream stream) 39 | { 40 | IJSInProcessObjectReference inProcessHelper = await jSRuntime.GetInProcessHelperAsync(); 41 | IJSInProcessObjectReference jSInstance = await inProcessHelper.InvokeAsync("constructWritableStreamDefaultReader", stream.JSReference); 42 | return new WritableStreamDefaultWriterInProcess(jSRuntime, inProcessHelper, jSInstance, new() { DisposesJSReference = true }); 43 | } 44 | 45 | /// 46 | protected internal WritableStreamDefaultWriterInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference inProcessHelper, IJSInProcessObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) 47 | { 48 | this.inProcessHelper = inProcessHelper; 49 | JSReference = jSReference; 50 | } 51 | 52 | /// 53 | /// The size desired to fill the writers internal queue. It can be negative, if the queue is over-full. 54 | /// 55 | /// It will be null if the stream cannot be successfully written to (due to either being errored, or having an abort queued up). It will return zero if the stream is closed. 56 | public double? DesiredSize => inProcessHelper.Invoke("getAttribute", JSReference, "desiredSize"); 57 | 58 | /// 59 | /// Releases the writer's lock. 60 | /// 61 | /// 62 | public void ReleaseLock() 63 | { 64 | JSReference.InvokeVoid("releaseLock"); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/WritableStream/WritableStreamDefaultWriter.cs: -------------------------------------------------------------------------------- 1 | using KristofferStrube.Blazor.WebIDL; 2 | using Microsoft.JSInterop; 3 | 4 | namespace KristofferStrube.Blazor.Streams; 5 | 6 | /// 7 | /// Streams browser specs 8 | /// 9 | public class WritableStreamDefaultWriter : BaseJSWrapper, IJSCreatable 10 | { 11 | /// 12 | /// Constructs a wrapper instance for a given JS Instance of a . 13 | /// 14 | /// An instance. 15 | /// A JS reference to an existing . 16 | /// A wrapper instance for a . 17 | [Obsolete("This will be removed in the next major release as all creator methods should be asynchronous for uniformity. Use CreateAsync instead.")] 18 | public static WritableStreamDefaultWriter Create(IJSRuntime jSRuntime, IJSObjectReference jSReference) 19 | { 20 | return new WritableStreamDefaultWriter(jSRuntime, jSReference, new()); 21 | } 22 | 23 | /// 24 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference) 25 | { 26 | return await CreateAsync(jSRuntime, jSReference, new()); 27 | } 28 | 29 | /// 30 | public static Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) 31 | { 32 | return Task.FromResult(new WritableStreamDefaultWriter(jSRuntime, jSReference, options)); 33 | } 34 | 35 | /// 36 | /// Constructs a from some . 37 | /// 38 | /// An IJSRuntime instance. 39 | /// A wrapper instance. 40 | /// 41 | public static async Task CreateAsync(IJSRuntime jSRuntime, WritableStream stream) 42 | { 43 | IJSObjectReference helper = await jSRuntime.GetHelperAsync(); 44 | IJSObjectReference jSInstance = await helper.InvokeAsync("constructWritableStreamDefaultReader", stream.JSReference); 45 | return new WritableStreamDefaultWriter(jSRuntime, jSInstance, new() { DisposesJSReference = true }); 46 | } 47 | 48 | /// 49 | protected WritableStreamDefaultWriter(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) { } 50 | 51 | /// 52 | /// A JS reference to the promise related to closing the writer. 53 | /// 54 | /// 55 | public async Task GetClosedAsync() 56 | { 57 | IJSObjectReference helper = await helperTask.Value; 58 | return await helper.InvokeAsync("getAttribute", JSReference, "closed"); 59 | } 60 | 61 | /// 62 | /// The size desired to fill the writers internal queue. It can be negative, if the queue is over-full. 63 | /// 64 | /// It will be null if the stream cannot be successfully written to (due to either being errored, or having an abort queued up). It will return zero if the stream is closed. 65 | public async Task GetDesiredSizeAsync() 66 | { 67 | IJSObjectReference helper = await helperTask.Value; 68 | return await helper.InvokeAsync("getAttribute", JSReference, "desiredSize"); 69 | } 70 | 71 | /// 72 | /// A JS reference to a promise that will be fulfilled when the changes to a positive number meaning the internal queue of the writer is ready to receive data. 73 | /// 74 | /// 75 | public async Task GetReadyAsync() 76 | { 77 | IJSObjectReference helper = await helperTask.Value; 78 | return await helper.InvokeAsync("getAttribute", JSReference, "ready"); 79 | } 80 | 81 | /// 82 | /// Aborts the stream. The same as . 83 | /// 84 | /// 85 | public async Task AbortAsync() 86 | { 87 | await JSReference.InvokeVoidAsync("abort"); 88 | } 89 | 90 | /// 91 | /// Closes the stream. The same as . 92 | /// 93 | /// 94 | public async Task CloseAsync() 95 | { 96 | await JSReference.InvokeVoidAsync("close"); 97 | } 98 | 99 | /// 100 | /// Releases the writer's lock. 101 | /// 102 | /// 103 | public async Task ReleaseLockAsync() 104 | { 105 | await JSReference.InvokeVoidAsync("releaseLock"); 106 | } 107 | 108 | /// 109 | /// Writes the chunk to the writable stream once any previous writes have finished successfully. 110 | /// 111 | /// 112 | /// 113 | public async Task WriteAsync(IJSObjectReference chunk) 114 | { 115 | await JSReference.InvokeVoidAsync("write", chunk); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Web 2 | -------------------------------------------------------------------------------- /src/KristofferStrube.Blazor.Streams/wwwroot/KristofferStrube.Blazor.Streams.js: -------------------------------------------------------------------------------- 1 | export function getAttribute(object, attribute) { return object[attribute]; } 2 | 3 | export function setAttribute(object, attribute, value) { return object[attribute] = value; } 4 | 5 | export function elementAt(array, index) { return array.at(index); } 6 | 7 | export function constructReadableStreamDefaultReader(stream) { 8 | return new ReadableStreamDefaultReader(stream); 9 | } 10 | 11 | export function constructReadableStreamBYOBReader(stream) { 12 | return new ReadableStreamBYOBReader(stream); 13 | } 14 | 15 | export function constructReadableWritablePair(readable, writable) { 16 | return { readable: readable, writable: writable }; 17 | } 18 | 19 | export function constructReadableStream(underlyingSource, strategy) { 20 | if (underlyingSource == null) { 21 | if (strategy == null) { 22 | return new ReadableStream(); 23 | } 24 | return new ReadableStream(null, queueingStrategy(strategy)); 25 | } 26 | var source = { 27 | start(controller) { 28 | underlyingSource.objRef.invokeMethodAsync('InvokeStart', DotNet.createJSObjectReference(controller)); 29 | }, 30 | pull(controller) { 31 | underlyingSource.objRef.invokeMethodAsync('InvokePull', DotNet.createJSObjectReference(controller)); 32 | }, 33 | cancel() { 34 | underlyingSource.objRef.invokeMethodAsync('InvokeCancel'); 35 | }, 36 | }; 37 | if (strategy == null) { 38 | return new ReadableStream(source); 39 | } 40 | return new ReadableStream(source, queueingStrategy(strategy)); 41 | } 42 | 43 | export function constructWritableStream(underlyingSink, strategy) { 44 | if (underlyingSink == null) { 45 | return new WritableStream(null, queueingStrategy(strategy)); 46 | } 47 | var sink = { 48 | start(controller) { 49 | underlyingSink.objRef.invokeMethodAsync('InvokeStart', DotNet.createJSObjectReference(controller)); 50 | }, 51 | write(chunk, controller) { 52 | underlyingSink.objRef.invokeMethodAsync('InvokeWrite', DotNet.createJSObjectReference(chunk), DotNet.createJSObjectReference(controller)); 53 | }, 54 | close() { 55 | underlyingSink.objRef.invokeMethodAsync('InvokeClose'); 56 | }, 57 | abort() { 58 | underlyingSink.objRef.invokeMethodAsync('InvokeAbort'); 59 | }, 60 | }; 61 | return new WritableStream(sink, queueingStrategy(strategy)); 62 | } 63 | 64 | export function constructTransformStream(transformer, writableStrategy, readableStrategy) { 65 | if (underlyingSink == null) { 66 | return new TransformStream(null, queueingStrategy(writableStrategy), queueingStrategy(readableStrategy)); 67 | } 68 | var constructedTransformer = { 69 | start(controller) { 70 | underlyingSink.objRef.invokeMethodAsync('InvokeStart', DotNet.createJSObjectReference(controller)); 71 | }, 72 | transform(chunk, controller) { 73 | underlyingSink.objRef.invokeMethodAsync('InvokeTransform', DotNet.createJSObjectReference(chunk), DotNet.createJSObjectReference(controller)); 74 | }, 75 | flush(controller) { 76 | underlyingSink.objRef.invokeMethodAsync('InvokeFlush', DotNet.createJSObjectReference(controller)); 77 | }, 78 | cancel(reason) { 79 | underlyingSink.objRef.invokeMethodAsync('InvokeCancel', DotNet.createJSObjectReference(reason)); 80 | }, 81 | }; 82 | return new TransformStream(constructedTransformer, queueingStrategy(writableStrategy), queueingStrategy(readableStrategy)); 83 | } 84 | 85 | function queueingStrategy(strategy) { 86 | if (strategy == null) { 87 | return {}; 88 | } 89 | if (strategy instanceof ByteLengthQueuingStrategy || strategy instanceof CountQueuingStrategy) { 90 | return strategy; 91 | } 92 | return { 93 | highWaterMark: strategy.highWaterMark, 94 | size: (chunk) => strategy.objRef.invokeMethod('InvokeSize', DotNet.createJSObjectReference(chunk)) 95 | }; 96 | } 97 | 98 | export function constructWritableStreamDefaultReader(stream) { 99 | return new WritableStreamDefaultReader(stream); 100 | } 101 | 102 | export function constructByteLengthQueuingStrategy(init) { 103 | return new ByteLengthQueuingStrategy(init); 104 | } 105 | 106 | export function constructCountQueuingStrategy(init) { 107 | return new CountQueuingStrategy(init); 108 | } 109 | 110 | export function constructByteArray(size) { 111 | return new Uint8Array(size); 112 | } 113 | 114 | export function byteArray(object) { 115 | var bytes = new Uint8Array(object); 116 | return bytes; 117 | } 118 | 119 | export function valueOf(object) { 120 | return object; 121 | } --------------------------------------------------------------------------------