6 |
7 | Welcome to your new app.
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # CA2018: 'Buffer.BlockCopy' expects the number of bytes to be copied for the 'count' argument
4 | dotnet_diagnostic.CA2018.severity = none
5 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/Images/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LostBeard/SpawnDev.BlazorJS.WebWorkers/main/SpawnDev.BlazorJS.WebWorkers/Images/icon-128.png
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.Demo/wwwroot/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LostBeard/SpawnDev.BlazorJS.WebWorkers/main/SpawnDev.BlazorJS.WebWorkers.Demo/wwwroot/favicon.png
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.Demo/wwwroot/icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LostBeard/SpawnDev.BlazorJS.WebWorkers/main/SpawnDev.BlazorJS.WebWorkers.Demo/wwwroot/icon-192.png
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/buildcontent/patch-post.min.js:
--------------------------------------------------------------------------------
1 | globalThis.blazorConfig&&globalThis.blazorConfig.autoStart&&(globalThis.blazorConfig.webAssemblyConfig?Blazor.start(globalThis.blazorConfig.webAssemblyConfig):Blazor.start());
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/TransferableListAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace SpawnDev.BlazorJS.WebWorkers
2 | {
3 | ///
4 | /// Use this attribute to mark a parameter as a transferable list indicating what objects to transfer when posting to a web worker.
5 | ///
6 | [AttributeUsage(AttributeTargets.Parameter)]
7 | public class TransferableListAttribute : Attribute
8 | {
9 |
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.Demo/Layout/MainLayout.razor:
--------------------------------------------------------------------------------
1 | @inherits LayoutComponentBase
2 |
17 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.Demo/wwwroot/app.worker.module.globals.js:
--------------------------------------------------------------------------------
1 | // anything that needs to be set or run before other stuff must be in the first statically imported script because
2 | // static imports in the main file are loaded before any code, no matter the placement of the code or imports.
3 |
4 | // this tells spawndev.blazorjs.webworkers.module.js not to start Blazor. We will start it with Blazor.start() and optionally with any startup changes we need.
5 | globalThis.autoStart = false;
6 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/AssetManifest.cs:
--------------------------------------------------------------------------------
1 | namespace SpawnDev.BlazorJS.WebWorkers
2 | {
3 | ///
4 | /// Asset manifest
5 | ///
6 | public class AssetManifest
7 | {
8 | ///
9 | /// Assets
10 | ///
11 | public List Assets { get; set; }
12 | ///
13 | /// Version
14 | ///
15 | public string Version { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.Demo/_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 SpawnDev.BlazorJS.WebWorkers.Demo
10 | @using SpawnDev.BlazorJS.WebWorkers.Demo.Layout
11 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.Demo/wwwroot/app.service-worker.module.globals.js:
--------------------------------------------------------------------------------
1 | // anything that needs to be set or run before other stuff must be in the first statically imported script because
2 | // static imports in the main file are loaded before any code, no matter the placement of the code or imports.
3 |
4 | // this tells spawndev.blazorjs.webworkers.module.js not to start Blazor. We will start it with Blazor.start() and optionally with any startup changes we need.
5 | globalThis.autoStart = false;
6 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.Demo/App.razor:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Not found
8 |
9 |
Sorry, there's nothing at this address.
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.Demo/wwwroot/app.worker.module.js:
--------------------------------------------------------------------------------
1 | // A custom service worker or web worker startup script can be used to static import Javascript libraries.
2 | // This was specifically tested to allow Blazor WASM to run in a Chrome Browser extension background ServiceWorker running in 'module' mode so it can run Transformers.js
3 |
4 | import * as _globals from "./app.worker.module.globals.js"
5 | import * as _importShim from "./spawndev.blazorjs.webworkers.module.js"
6 |
7 | // Start Blazor
8 | Blazor._startTask ??= Blazor.start();
9 |
10 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.Demo/wwwroot/app.service-worker.module.js:
--------------------------------------------------------------------------------
1 | // A custom service worker or web worker startup script can be used to static import Javascript libraries.
2 | // This was specifically tested to allow Blazor WASM to run in a Chrome Browser extension background ServiceWorker running in 'module' mode so it can run Transformers.js
3 |
4 | import * as _globals from "./app.service-worker.module.globals.js"
5 | import * as _blazorWorker from "./spawndev.blazorjs.webworkers.module.js"
6 |
7 | // Start Blazor
8 | Blazor._startTask ??= Blazor.start();
9 |
10 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/ManifestAsset.cs:
--------------------------------------------------------------------------------
1 | namespace SpawnDev.BlazorJS.WebWorkers
2 | {
3 | ///
4 | /// Manifest asset info
5 | ///
6 | public class ManifestAsset
7 | {
8 | ///
9 | /// File content hash. This should be the base-64-formatted SHA256 value.
10 | ///
11 | public string Hash { get; set; }
12 | ///
13 | /// Asset URL. Normally this will be relative to the application's base href.
14 | ///
15 | public string Url { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/buildcontent/patch-post.js:
--------------------------------------------------------------------------------
1 | //var locationPathname = new URL(globalThis.location.href).pathname;
2 | //var locationFilename = locationPathname.substring(locationPathname.lastIndexOf('/') + 1);
3 | //if (locationFilename.indexOf('blazor.webassembly.js') !== -1) {
4 | // globalThis.blazorConfig.autoStart = true;
5 | //}
6 | if (globalThis.blazorConfig && globalThis.blazorConfig.autoStart) {
7 | if (globalThis.blazorConfig.webAssemblyConfig) {
8 | Blazor.start(globalThis.blazorConfig.webAssemblyConfig);
9 | } else {
10 | Blazor.start();
11 | }
12 | }
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.Demo/wwwroot/sample-data/weather.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "date": "2022-01-06",
4 | "temperatureC": 1,
5 | "summary": "Freezing"
6 | },
7 | {
8 | "date": "2022-01-07",
9 | "temperatureC": 14,
10 | "summary": "Bracing"
11 | },
12 | {
13 | "date": "2022-01-08",
14 | "temperatureC": -13,
15 | "summary": "Freezing"
16 | },
17 | {
18 | "date": "2022-01-09",
19 | "temperatureC": -16,
20 | "summary": "Balmy"
21 | },
22 | {
23 | "date": "2022-01-10",
24 | "temperatureC": -2,
25 | "summary": "Chilly"
26 | }
27 | ]
28 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/ServiceWorkerStartupRegistration.cs:
--------------------------------------------------------------------------------
1 | namespace SpawnDev.BlazorJS.WebWorkers
2 | {
3 | ///
4 | /// ServiceWorkerStartupRegistration enum
5 | ///
6 | public enum ServiceWorkerStartupRegistration
7 | {
8 | ///
9 | /// Do nothing
10 | ///
11 | None,
12 | ///
13 | /// Register the service worker
14 | ///
15 | Register,
16 | ///
17 | /// Unregister the service worker
18 | ///
19 | Unregister,
20 | }
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/MissedEvents/MissedSyncEvent.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using SpawnDev.BlazorJS.JSObjects;
3 |
4 | namespace SpawnDev.BlazorJS.WebWorkers
5 | {
6 | internal class MissedSyncEvent : SyncEvent, IMissedEvent
7 | {
8 | ///
9 | public MissedSyncEvent(IJSInProcessObjectReference _ref) : base(_ref) { }
10 | ///
11 | public void WaitResolve() => JSRef!.CallVoid("waitResolve");
12 | ///
13 | public void WaitReject() => JSRef!.CallVoid("waitReject");
14 | ///
15 | public bool IsExtended => !JSRef!.IsUndefined("waitResolve");
16 | }
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/MissedEvents/MissedPeriodicSyncEvent.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using SpawnDev.BlazorJS.JSObjects;
3 |
4 | namespace SpawnDev.BlazorJS.WebWorkers
5 | {
6 | internal class MissedPeriodicSyncEvent : PeriodicSyncEvent, IMissedEvent
7 | {
8 | ///
9 | public MissedPeriodicSyncEvent(IJSInProcessObjectReference _ref) : base(_ref) { }
10 | ///
11 | public void WaitResolve() => JSRef!.CallVoid("waitResolve");
12 | ///
13 | public void WaitReject() => JSRef!.CallVoid("waitReject");
14 | ///
15 | public bool IsExtended => !JSRef!.IsUndefined("waitResolve");
16 | }
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/ServiceCallDispatcherInfo.cs:
--------------------------------------------------------------------------------
1 | namespace SpawnDev.BlazorJS.WebWorkers
2 | {
3 | ///
4 | /// Basic instance information
5 | ///
6 | public class ServiceCallDispatcherInfo
7 | {
8 | ///
9 | /// From the instance's BlazorJSRuntime.InstanceId property
10 | ///
11 | public string InstanceId { get; init; } = "";
12 | ///
13 | /// The Javascript globalThis class name
14 | /// - Window
15 | /// - DedicatedWorkerGlobalScope
16 | /// - SharedWorkerGlobalScope
17 | /// - ServiceWorkerGlobalScope
18 | ///
19 | public string GlobalThisTypeName { get; init; } = "";
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.Build/Tasks/FileHasher.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Security.Cryptography;
4 | using System.Text;
5 |
6 | namespace SpawnDev.BlazorJS.WebWorkers.Build.Tasks;
7 |
8 | public static class FileHasher
9 | {
10 | public static string GetStringHashBase64(string text)
11 | {
12 | using var hash = SHA256.Create();
13 | var bytes = Encoding.UTF8.GetBytes(text);
14 | var hashBytes = hash.ComputeHash(bytes);
15 | return Convert.ToBase64String(hashBytes);
16 | }
17 | public static string GetFileHashBase64(string filePath)
18 | {
19 | var bytes = File.ReadAllBytes(filePath);
20 | using var hash = SHA256.Create();
21 | var hashBytes = hash.ComputeHash(bytes);
22 | return Convert.ToBase64String(hashBytes);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/OriginCallableAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace SpawnDev.BlazorJS.WebWorkers
2 | {
3 | ///
4 | /// Used to modify call options for SpawnDev.BlazorJS.WebWorkers.ServiceCallDispatcher
5 | ///
6 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
7 | public class OriginCallableAttribute : Attribute
8 | {
9 | ///
10 | /// Methods with NoReply = true will not send exceptions or results back to the caller
11 | /// That makes NoReply calls quicker and useful for broadcast/event messages
12 | /// This should only be used on methods that return void or Task (un-typed) and
13 | /// there may be issues with serialization of some types
14 | ///
15 | public bool NoReply { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/MissedEvents/MissedPushEvent.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using SpawnDev.BlazorJS.JSObjects;
3 |
4 | namespace SpawnDev.BlazorJS.WebWorkers
5 | {
6 | ///
7 | /// An Event that was initially missed while Blazor was loading, but was held using waitUntil() so that Blazor can handle it.
8 | ///
9 | internal class MissedPushEvent : PushEvent, IMissedEvent
10 | {
11 | ///
12 | public MissedPushEvent(IJSInProcessObjectReference _ref) : base(_ref) { }
13 | ///
14 | public void WaitResolve() => JSRef!.CallVoid("waitResolve");
15 | ///
16 | public void WaitReject() => JSRef!.CallVoid("waitReject");
17 | ///
18 | public bool IsExtended => !JSRef!.IsUndefined("waitResolve");
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/MissedEvents/MissedContentIndexEvent.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using SpawnDev.BlazorJS.JSObjects;
3 |
4 | namespace SpawnDev.BlazorJS.WebWorkers
5 | {
6 | ///
7 | /// An Event that was initially missed while Blazor was loading, but was held using waitUntil() so that Blazor can handle it.
8 | ///
9 | internal class MissedContentIndexEvent : ContentIndexEvent, IMissedEvent
10 | {
11 | ///
12 | public MissedContentIndexEvent(IJSInProcessObjectReference _ref) : base(_ref) { }
13 | ///
14 | public void WaitResolve() => JSRef!.CallVoid("waitResolve");
15 | ///
16 | public void WaitReject() => JSRef!.CallVoid("waitReject");
17 | ///
18 | public bool IsExtended => !JSRef!.IsUndefined("waitResolve");
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/MissedEvents/MissedExtendableEvent.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using SpawnDev.BlazorJS.JSObjects;
3 |
4 | namespace SpawnDev.BlazorJS.WebWorkers
5 | {
6 | ///
7 | /// An ExtendableEvent that was initially missed while Blazor was loading, but was held using waitUntil() so that Blazor can handle it.
8 | ///
9 | internal class MissedExtendableEvent : ExtendableEvent, IMissedEvent
10 | {
11 | ///
12 | public MissedExtendableEvent(IJSInProcessObjectReference _ref) : base(_ref) { }
13 | ///
14 | public void WaitResolve() => JSRef!.CallVoid("waitResolve");
15 | ///
16 | public void WaitReject() => JSRef!.CallVoid("waitReject");
17 | ///
18 | public bool IsExtended => !JSRef!.IsUndefined("waitResolve");
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/MissedEvents/MissedNotificationEvent.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using SpawnDev.BlazorJS.JSObjects;
3 |
4 | namespace SpawnDev.BlazorJS.WebWorkers
5 | {
6 | ///
7 | /// An Event that was initially missed while Blazor was loading, but was held using waitUntil() so that Blazor can handle it.
8 | ///
9 | internal class MissedNotificationEvent : NotificationEvent, IMissedEvent
10 | {
11 | ///
12 | public MissedNotificationEvent(IJSInProcessObjectReference _ref) : base(_ref) { }
13 | ///
14 | public void WaitResolve() => JSRef!.CallVoid("waitResolve");
15 | ///
16 | public void WaitReject() => JSRef!.CallVoid("waitReject");
17 | ///
18 | public bool IsExtended => !JSRef!.IsUndefined("waitResolve");
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/MissedEvents/MissedPaymentRequestEvent.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using SpawnDev.BlazorJS.JSObjects;
3 |
4 | namespace SpawnDev.BlazorJS.WebWorkers
5 | {
6 | ///
7 | /// An Event that was initially missed while Blazor was loading, but was held using waitUntil() so that Blazor can handle it.
8 | ///
9 | internal class MissedPaymentRequestEvent : PaymentRequestEvent, IMissedEvent
10 | {
11 | ///
12 | public MissedPaymentRequestEvent(IJSInProcessObjectReference _ref) : base(_ref) { }
13 | ///
14 | public void WaitResolve() => JSRef!.CallVoid("waitResolve");
15 | ///
16 | public void WaitReject() => JSRef!.CallVoid("waitReject");
17 | ///
18 | public bool IsExtended => !JSRef!.IsUndefined("waitResolve");
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/MissedEvents/MissedBackgroundFetchEvent.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using SpawnDev.BlazorJS.JSObjects;
3 |
4 | namespace SpawnDev.BlazorJS.WebWorkers
5 | {
6 | ///
7 | /// An Event that was initially missed while Blazor was loading, but was held using waitUntil() so that Blazor can handle it.
8 | ///
9 | internal class MissedBackgroundFetchEvent : BackgroundFetchEvent, IMissedEvent
10 | {
11 | ///
12 | public MissedBackgroundFetchEvent(IJSInProcessObjectReference _ref) : base(_ref) { }
13 | ///
14 | public void WaitResolve() => JSRef!.CallVoid("waitResolve");
15 | ///
16 | public void WaitReject() => JSRef!.CallVoid("waitReject");
17 | ///
18 | public bool IsExtended => !JSRef!.IsUndefined("waitResolve");
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/WorkerTransferAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace SpawnDev.BlazorJS.WebWorkers
2 | {
3 | ///
4 | /// The WorkerTransferAttribute is used to mark values that should be added to the transferred list when send to another context
5 | ///
6 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.ReturnValue | AttributeTargets.Parameter, Inherited = false, AllowMultiple = true)]
7 | public class WorkerTransferAttribute : Attribute
8 | {
9 | ///
10 | /// If true, the value will be added to the transferable list
11 | ///
12 | public bool Transfer { get; private set; } = true;
13 | ///
14 | /// New instance
15 | ///
16 | ///
17 | public WorkerTransferAttribute(bool transfer = true) => Transfer = transfer;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.Build/SpawnDev.BlazorJS.WebWorkers.Build.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | 10.0
6 | ..\SpawnDev.BlazorJS.WebWorkers\tasks\netstandard2.0
7 | true
8 | 2.5.5
9 |
10 |
11 |
12 | False
13 |
14 |
15 |
16 | False
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/MissedEvents/MissedExtendableMessageEvent.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using SpawnDev.BlazorJS.JSObjects;
3 |
4 | namespace SpawnDev.BlazorJS.WebWorkers
5 | {
6 | ///
7 | /// An Event that was initially missed while Blazor was loading, but was held using waitUntil() so that Blazor can handle it.
8 | ///
9 | internal class MissedExtendableMessageEvent : ExtendableMessageEvent, IMissedEvent
10 | {
11 | ///
12 | public MissedExtendableMessageEvent(IJSInProcessObjectReference _ref) : base(_ref) { }
13 | ///
14 | public void WaitResolve() => JSRef!.CallVoid("waitResolve");
15 | ///
16 | public void WaitReject() => JSRef!.CallVoid("waitReject");
17 | ///
18 | public bool IsExtended => !JSRef!.IsUndefined("waitResolve");
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [LostBeard] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12 | polar: # Replace with a single Polar username
13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
14 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
15 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/MissedEvents/IMissedEvent.cs:
--------------------------------------------------------------------------------
1 | namespace SpawnDev.BlazorJS.WebWorkers
2 | {
3 | ///
4 | /// An ExtendableEvent that was initially missed while Blazor was loading, but was held using waitUntil() so that Blazor can handle it.
5 | /// Implementaors will be able to use the WaitResolve and WaitReject methods to resolve or reject the event.
6 | ///
7 | public interface IMissedEvent
8 | {
9 | ///
10 | /// Resolves the missed ExtendableEvent.
11 | ///
12 | void WaitResolve();
13 | ///
14 | /// Rejects the missed ExtendableEvent.
15 | ///
16 | void WaitReject();
17 | ///
18 | /// Returns true if the event is an ExtendableEvent.
19 | ///
20 | bool IsExtended { get; }
21 | }
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/MissedEvents/MissedPushSubscriptionChangeEvent.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using SpawnDev.BlazorJS.JSObjects;
3 |
4 | namespace SpawnDev.BlazorJS.WebWorkers
5 | {
6 | ///
7 | /// An Event that was initially missed while Blazor was loading, but was held using waitUntil() so that Blazor can handle it.
8 | ///
9 | internal class MissedPushSubscriptionChangeEvent: PushSubscriptionChangeEvent, IMissedEvent
10 | {
11 | ///
12 | public MissedPushSubscriptionChangeEvent(IJSInProcessObjectReference _ref) : base(_ref) { }
13 | ///
14 | public void WaitResolve() => JSRef!.CallVoid("waitResolve");
15 | ///
16 | public void WaitReject() => JSRef!.CallVoid("waitReject");
17 | ///
18 | public bool IsExtended => !JSRef!.IsUndefined("waitResolve");
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/MissedEvents/MissedBackgroundFetchUpdateUIEvent.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using SpawnDev.BlazorJS.JSObjects;
3 |
4 | namespace SpawnDev.BlazorJS.WebWorkers
5 | {
6 | ///
7 | /// An Event that was initially missed while Blazor was loading, but was held using waitUntil() so that Blazor can handle it.
8 | ///
9 | internal class MissedBackgroundFetchUpdateUIEvent : BackgroundFetchUpdateUIEvent, IMissedEvent
10 | {
11 | ///
12 | public MissedBackgroundFetchUpdateUIEvent(IJSInProcessObjectReference _ref) : base(_ref) { }
13 | ///
14 | public void WaitResolve() => JSRef!.CallVoid("waitResolve");
15 | ///
16 | public void WaitReject() => JSRef!.CallVoid("waitReject");
17 | ///
18 | public bool IsExtended => !JSRef!.IsUndefined("waitResolve");
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/MissedEvents/MissedExtendableCookieChangeEvent.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using SpawnDev.BlazorJS.JSObjects;
3 |
4 | namespace SpawnDev.BlazorJS.WebWorkers
5 | {
6 | ///
7 | /// An Event that was initially missed while Blazor was loading, but was held using waitUntil() so that Blazor can handle it.
8 | ///
9 | internal class MissedExtendableCookieChangeEvent : ExtendableCookieChangeEvent, IMissedEvent
10 | {
11 | ///
12 | public MissedExtendableCookieChangeEvent(IJSInProcessObjectReference _ref) : base(_ref) { }
13 | ///
14 | public void WaitResolve() => JSRef!.CallVoid("waitResolve");
15 | ///
16 | public void WaitReject() => JSRef!.CallVoid("waitReject");
17 | ///
18 | public bool IsExtended => !JSRef!.IsUndefined("waitResolve");
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/ClaimsIdentityExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 |
3 | namespace SpawnDev.BlazorJS.WebWorkers
4 | {
5 | internal static class ClaimsIdentityExtensions
6 | {
7 | public static string ToBase64(this ClaimsIdentity claimsIdentity)
8 | {
9 | using var buffer = new System.IO.MemoryStream();
10 | using var writer = new System.IO.BinaryWriter(buffer);
11 | claimsIdentity.WriteTo(writer);
12 | var data = buffer.ToArray();
13 | return Convert.ToBase64String(data);
14 | }
15 | public static ClaimsIdentity Base64ToClaimsIdentity(this string claimsIdentity)
16 | {
17 | var data = Convert.FromBase64String(claimsIdentity);
18 | using var buffer = new System.IO.MemoryStream(data);
19 | using var reader = new System.IO.BinaryReader(buffer);
20 | return new ClaimsIdentity(reader);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/WebWorkerOptions.cs:
--------------------------------------------------------------------------------
1 | using SpawnDev.BlazorJS.JSObjects;
2 |
3 | namespace SpawnDev.BlazorJS.WebWorkers
4 | {
5 | ///
6 | /// Options used for CreateWebWorker
7 | ///
8 | public class WebWorkerOptions
9 | {
10 | ///
11 | /// WorkerOptions
12 | ///
13 | public WorkerOptions? WorkerOptions { get; set; }
14 | ///
15 | /// The URL to the worker script to load.
16 | /// Defaults to:
17 | /// module - "spawndev.blazorjs.webworkers.module.js"
18 | /// classic - "spawndev.blazorjs.webworkers.js"
19 | ///
20 | public string? ScriptUrl { get; set; } = null;
21 | ///
22 | /// Additional query parameters to add to the worker script URL.
23 | ///
24 | public Dictionary? QueryParams { get; set; } = null;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/SharedWebWorkerOptions.cs:
--------------------------------------------------------------------------------
1 | using SpawnDev.BlazorJS.JSObjects;
2 |
3 | namespace SpawnDev.BlazorJS.WebWorkers
4 | {
5 | ///
6 | /// Options used for CreateWebWorker
7 | ///
8 | public class SharedWebWorkerOptions
9 | {
10 | ///
11 | /// WorkerOptions
12 | ///
13 | public SharedWorkerOptions? WorkerOptions { get; set; }
14 | ///
15 | /// The URL to the worker script to load.
16 | /// Defaults to:
17 | /// module - "spawndev.blazorjs.webworkers.module.js"
18 | /// classic - "spawndev.blazorjs.webworkers.js"
19 | ///
20 | public string? ScriptUrl { get; set; } = null;
21 | ///
22 | /// Additional query parameters to add to the worker script URL.
23 | ///
24 | public Dictionary? QueryParams { get; set; } = null;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/SharedWebWorker.cs:
--------------------------------------------------------------------------------
1 | using SpawnDev.BlazorJS.JSObjects;
2 |
3 | namespace SpawnDev.BlazorJS.WebWorkers
4 | {
5 | public class SharedWebWorker : ServiceCallDispatcher, IDisposable
6 | {
7 | public static bool Supported;
8 | static SharedWebWorker()
9 | {
10 | Supported = !JS.IsUndefined("SharedWorker");
11 | }
12 | SharedWorker _shareWorker { get; set; }
13 | public string Name { get; }
14 | public SharedWebWorker(string name, SharedWorker sharedWorker, IBackgroundServiceManager webAssemblyServices) : base(webAssemblyServices, sharedWorker.Port)
15 | {
16 | Name = name;
17 | _shareWorker = sharedWorker;
18 | if (_port is MessagePort port) port.Start();
19 | }
20 |
21 | protected override void Dispose(bool disposing)
22 | {
23 | if (IsDisposed) return;
24 | _shareWorker?.Dispose();
25 | _port?.Dispose();
26 | base.Dispose(disposing);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/MissedEvents/MissedFetchEvent.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using SpawnDev.BlazorJS.JSObjects;
3 |
4 | namespace SpawnDev.BlazorJS.WebWorkers
5 | {
6 | ///
7 | /// An Event that was initially missed while Blazor was loading, but was held using waitUntil() so that Blazor can handle it.
8 | ///
9 | internal class MissedFetchEvent : FetchEvent
10 | {
11 | ///
12 | public MissedFetchEvent(IJSInProcessObjectReference _ref) : base(_ref) { }
13 | ///
14 | /// Resolves the FetchEvent.
15 | ///
16 | ///
17 | public void ResponseResolve(Response response) => JSRef!.CallVoid("responseResolve", response);
18 | ///
19 | /// Rejects the FetchEvent.
20 | ///
21 | public void ResponseReject() => JSRef!.CallVoid("responseReject");
22 | ///
23 | public bool IsExtended => !JSRef!.IsUndefined("responseResolve");
24 | }
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) [year] [fullname]
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 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/MissedEvents/MissedCanMakePaymentEvent.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using SpawnDev.BlazorJS.JSObjects;
3 |
4 | namespace SpawnDev.BlazorJS.WebWorkers
5 | {
6 | ///
7 | /// An Event that was initially missed while Blazor was loading, but was held using waitUntil() so that Blazor can handle it.
8 | ///
9 | internal class MissedCanMakePaymentEvent : CanMakePaymentEvent
10 | {
11 | ///
12 | public MissedCanMakePaymentEvent(IJSInProcessObjectReference _ref) : base(_ref) { }
13 | ///
14 | /// Resolves the CanMakePaymentEvent.
15 | ///
16 | ///
17 | public void ResponseResolve(bool response) => JSRef!.CallVoid("responseResolve", response);
18 | ///
19 | /// Rejects the CanMakePaymentEvent.
20 | ///
21 | public void ResponseReject() => JSRef!.CallVoid("responseReject");
22 | ///
23 | public bool IsExtended => !JSRef!.IsUndefined("responseResolve");
24 | }
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/wwwroot/spawndev.blazorjs.webworkers.empty.js:
--------------------------------------------------------------------------------
1 | // to test dynamic import support this script will be loaded.
2 | // it is intentionally empty.
3 | // supports content-sceurity-policy that prohibits script-src from data: (previous test method)
4 |
5 | // dynamic import support tested using the below method which tries to load this (mostly) empty script
6 | //async function hasDynamicImport() {
7 | // try {
8 | // await import('empty.js');
9 | // return true;
10 | // } catch (e) {
11 | // return false;
12 | // }
13 | //}
14 |
15 | // previous test method that was not compatible with stricter CSPs
16 | //async function hasDynamicImport() {
17 | // // ServiceWorkers have issues with dynamic imports even if detection says it is detected as supported; may jsut not have been loaded usign 'module' keyword
18 | // if (browserExtension) {
19 | // return true;
20 | // }
21 | // if (globalThisTypeName == 'ServiceWorkerGlobalScope') {
22 | // return false;
23 | // }
24 | // try {
25 | // await import('data:text/javascript;base64,Cg==');
26 | // return true;
27 | // } catch (e) {
28 | // return false;
29 | // }
30 | //}
31 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/FromServicesAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace SpawnDev.BlazorJS.WebWorkers
2 | {
3 | ///
4 | /// Method parameters marked with the FromServices attribute will be resolved from the fulfilling side's service provider
5 | ///
6 | [AttributeUsage(AttributeTargets.Parameter)]
7 | public class FromServicesAttribute : Attribute { }
8 |
9 | #if !NET8_0_OR_GREATER
10 | [AttributeUsage(AttributeTargets.Parameter)]
11 | public class FromKeyedServicesAttribute : Attribute
12 | {
13 | ///
14 | /// Creates a new instance.
15 | ///
16 | /// The key of the keyed service to bind to.
17 | public FromKeyedServicesAttribute(object key) => Key = key;
18 |
19 | ///
20 | /// The key of the keyed service to bind to.
21 | ///
22 | public object Key { get; }
23 | }
24 | #endif
25 |
26 | ///
27 | /// Method parameters marked with the FromLocal attribute will be resolved from the fulfilling side
28 | ///
29 | [AttributeUsage(AttributeTargets.Parameter)]
30 | public class FromLocalAttribute : Attribute { }
31 | }
32 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/build/SpawnDev.BlazorJS.WebWorkers.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | false
8 | wwwroot
9 | $(Configuration)Extension
10 |
11 | <_DebugSpawnDevWebWorkersBuildTasks Condition="'$(_DebugSpawnDevWebWorkersBuildTasks)' == ''">false
12 | <_WebWorkerBuildTasksAssembly Condition="'$(_WebWorkerBuildTasksAssembly)' == ''">$(MSBuildThisFileDirectory)..\tasks\netstandard2.0\SpawnDev.BlazorJS.WebWorkers.Build.dll
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/RemoteCallableAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace SpawnDev.BlazorJS.WebWorkers
2 | {
3 | ///
4 | /// Used to modify call options for SpawnDev.BlazorJS.WebWorkers.RemoteDispatcher
5 | ///
6 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
7 | public class RemoteCallableAttribute : Attribute
8 | {
9 | ///
10 | /// Methods with NoReply = true will not send exceptions or results back to the caller
11 | /// That makes NoReply calls quicker and useful for broadcast/event messages
12 | /// This should only be used on methods that return void or Task (un-typed) and
13 | /// there may be issues with serialization of some types
14 | ///
15 | public bool NoReply { get; set; }
16 | ///
17 | /// Gets or sets a comma delimited list of roles that are allowed to access the resource.
18 | /// RemoteDispatcher.User will be checked for the specified roles.
19 | /// It is up to the implementing app to handle role management in RemoteDispatcher.User
20 | /// If no roles are set, no role based restrictions will be applied
21 | ///
22 | public string? Roles { get; set; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/WebWorker.cs:
--------------------------------------------------------------------------------
1 | using SpawnDev.BlazorJS.JSObjects;
2 |
3 | namespace SpawnDev.BlazorJS.WebWorkers
4 | {
5 | public class WebWorker : ServiceCallDispatcher, IDisposable
6 | {
7 | public static bool Supported;
8 | static WebWorker()
9 | {
10 | Supported = !JS.IsUndefined("Worker");
11 | }
12 | Worker _worker;
13 | public WebWorker(Worker worker, IBackgroundServiceManager webAssemblyServices) : base(webAssemblyServices, worker)
14 | {
15 | _worker = worker;
16 | }
17 | ///
18 | /// Called when being Disposed, before the disposal
19 | ///
20 | public event Action OnDisposing;
21 | ///
22 | /// Returns true if disposal has started
23 | ///
24 | public bool IsDisposing { get; protected set; } = false;
25 | protected override void Dispose(bool disposing)
26 | {
27 | if (IsDisposed) return;
28 | if (IsDisposing) return;
29 | IsDisposing = true;
30 | OnDisposing?.Invoke(this);
31 | try
32 | {
33 | _worker?.Terminate();
34 | }
35 | catch { }
36 | _worker?.Dispose();
37 | base.Dispose(disposing);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.Demo/wwwroot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | SpawnDev.BlazorJS.WebWorkers.Demo
8 |
9 |
10 |
11 |
12 |
13 |
19 |
20 |
21 |
22 |
23 |
27 |
28 |
29 |
30 |
31 | An unhandled error has occurred.
32 | Reload
33 | 🗙
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 |
4 | ## [2.5.22] - 2024-11-25
5 |
6 | ### Changed
7 | - Updated SpawnDev.BlazorJS dependency to 2.5.22
8 |
9 |
10 | ## [2.5.17] - 2024-11-15
11 |
12 | ### Changed
13 | - Updated SpawnDev.BlazorJS dependency to 2.5.17
14 |
15 | ### Fixed
16 | - Fixed JsonConverterAttribute with HybridConverter and HybridConverterFactory not working (Ex: SharedCancellationTokenSource)
17 |
18 |
19 | ## [2.5.16] - 2024-11-15
20 |
21 | ### Changed
22 | - Updated SpawnDev.BlazorJS dependency to 2.5.16
23 |
24 |
25 | ## [2.5.15] - 2024-11-15
26 |
27 | ### Changed
28 | - Updated SpawnDev.BlazorJS dependency to 2.5.15
29 |
30 |
31 | ## [2.5.14] - 2024-11-14
32 |
33 | ### Changed
34 | - Updated SpawnDev.BlazorJS dependency to 2.5.14
35 |
36 |
37 | ## [2.5.13] - 2024-11-12
38 |
39 | ### Changed
40 | - Updated package icon
41 | - Updated net9.0 Microsoft.AspNetCore.Components.WebAssembly dependency to 9.0.0
42 | - Updated SpawnDev.BlazorJS dependency to 2.5.13
43 |
44 |
45 | ## [2.5.12] - 2024-11-10
46 |
47 | ### Changed
48 | - Updated SpawnDev.BlazorJS dependency to 2.5.12
49 |
50 |
51 | ## [2.5.11] - 2024-10-31
52 |
53 | ### Fixed
54 | - Fixed WebWorkerService startup issue when running on the server. Allows registering the WebWorkerService on the server so that server side rendering and pre-rendering of components that use the WebWorkerService do not throw an error. The service will only be functional when using WASM rendering.
55 |
56 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/ServiceWorkerConfig.cs:
--------------------------------------------------------------------------------
1 | using SpawnDev.BlazorJS.JSObjects;
2 |
3 | namespace SpawnDev.BlazorJS.WebWorkers
4 | {
5 | ///
6 | /// Options for managing ServiceWorker registration
7 | ///
8 | public class ServiceWorkerConfig
9 | {
10 | ///
11 | /// The registration action to take when the app starts in a Window scope
12 | ///
13 | public ServiceWorkerStartupRegistration Register { get; set; } = ServiceWorkerStartupRegistration.Register;
14 | ///
15 | /// Defaults to WebWorkerService.WebWorkerJSScript ('spawndev.blazorjs.webworkers.js')
16 | ///
17 | public string? ScriptURL { get; set; }
18 | ///
19 | /// By default, this is "service-worker-assets.js"
20 | /// This should be the value from <ServiceWorkerAssetsManifest> in your project's .csproj file if different than the default
21 | ///
22 | public string? ServiceWorkerAssetsManifest { get; set; }
23 | ///
24 | /// This should be true if using <ServiceWorkerAssetsManifest> in your project's .csproj file
25 | ///
26 | public bool ImportServiceWorkerAssets { get; set; }
27 | ///
28 | /// Options used when registering a ServiceWorker via ServiceWorkerContainer.Register()
29 | ///
30 | public ServiceWorkerRegistrationOptions? Options { get; set; }
31 | }
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.Demo/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:63109",
8 | "sslPort": 44300
9 | }
10 | },
11 | "profiles": {
12 | "http": {
13 | "commandName": "Project",
14 | "dotnetRunMessages": true,
15 | "launchBrowser": true,
16 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
17 | "applicationUrl": "http://localhost:5030",
18 | "environmentVariables": {
19 | "ASPNETCORE_ENVIRONMENT": "Development"
20 | }
21 | },
22 | "https": {
23 | "commandName": "Project",
24 | "dotnetRunMessages": true,
25 | "launchBrowser": true,
26 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
27 | "applicationUrl": "https://localhost:7288;http://localhost:5030",
28 | "environmentVariables": {
29 | "ASPNETCORE_ENVIRONMENT": "Development"
30 | }
31 | },
32 | "IIS Express": {
33 | "commandName": "IISExpress",
34 | "launchBrowser": true,
35 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
36 | "environmentVariables": {
37 | "ASPNETCORE_ENVIRONMENT": "Development"
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.Demo/Pages/Weather.razor:
--------------------------------------------------------------------------------
1 | @page "/weather"
2 | @inject HttpClient Http
3 |
4 | Weather
5 |
6 |
Weather
7 |
8 |
This component demonstrates fetching data from the server.
34 |
35 | @code {
36 | private bool collapseNavMenu = true;
37 |
38 | private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
39 |
40 | private void ToggleNavMenu()
41 | {
42 | collapseNavMenu = !collapseNavMenu;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.Demo/SpawnDev.BlazorJS.WebWorkers.Demo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 | false
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
34 | false
35 |
36 |
37 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Deploy to GitHub Pages
2 |
3 | # Run workflow on every push to the master branch
4 | on: workflow_dispatch
5 |
6 | jobs:
7 | deploy-to-github-pages:
8 | permissions:
9 | contents: write
10 | # use ubuntu-latest image to run steps on
11 | runs-on: ubuntu-latest
12 | steps:
13 | # uses GitHub's checkout action to checkout code form the master branch
14 | - uses: actions/checkout@v2
15 |
16 | # sets up .NET Core SDK
17 | - name: Setup .NET Core SDK
18 | uses: actions/setup-dotnet@v3.0.3
19 | with:
20 | dotnet-version: 8.0.400-preview.0.24324.5
21 |
22 | # Install dotnet wasm buildtools workload
23 | - name: Install .NET WASM Build Tools
24 | run: dotnet workload install wasm-tools
25 |
26 | # publishes Blazor project to the publish-folder
27 | - name: Publish .NET Core Project
28 | run: dotnet publish ./SpawnDev.BlazorJS.WebWorkers.Demo/ --nologo -c:Release --output publish
29 |
30 | # changes the base-tag in index.html from '/' to '/SpawnDev.BlazorJS.WebWorkers/' to match GitHub Pages repository subdirectory
31 | - name: Change base-tag in index.html from / to /SpawnDev.BlazorJS.WebWorkers/
32 | run: sed -i 's/>> BlazorJS Running: {JS.GlobalScope.ToString()}");
13 |
14 | builder.Services.AddWebWorkerService(o =>
15 | {
16 | o.RestoreEnvironment = false; // true by default, set to false to leave the fake window environment in workers
17 | });
18 |
19 | builder.Services.RegisterServiceWorker(new ServiceWorkerConfig
20 | {
21 | Options = new ServiceWorkerRegistrationOptions
22 | {
23 | //Type = "module",
24 | },
25 | });
26 |
27 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
28 |
29 | if (JS.IsWindow)
30 | {
31 | builder.RootComponents.Add("#app");
32 | builder.RootComponents.Add("head::after");
33 | }
34 |
35 | var host = await builder.Build().StartBackgroundServices();
36 |
37 |
38 | JS.Set("_test", async (bool useModule) =>
39 | {
40 | var webWorkerService = host.Services.GetRequiredService();
41 | if (useModule)
42 | {
43 | var worker = await webWorkerService.GetWebWorker(new WebWorkerOptions { WorkerOptions = new WorkerOptions { Type = "module" } });
44 | await worker!.Run(() => Console.WriteLine("module worker"));
45 | }
46 | else
47 | {
48 | var worker = await webWorkerService.GetWebWorker();
49 | await worker!.Run(() => Console.WriteLine("classic worker"));
50 | }
51 | });
52 |
53 | //var arg = new SharedCancellationTokenSource();
54 | //var token = arg.Token;
55 |
56 | //JS.Set("_token1", token);
57 | //var token12 = JS.Get("_token1");
58 | //var keys = token12.JSRef?.Keys();
59 | //var token1 = JS.Get("_token1");
60 |
61 | await host.BlazorJSRunAsync();
62 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/wwwroot/spawndev.blazorjs.webworkers.module.js:
--------------------------------------------------------------------------------
1 | const params = new Proxy(new URLSearchParams(globalThis.location.search), { get: (searchParams, prop) => searchParams.get(prop), });
2 | if (globalThis.constructor?.name !== 'Window') globalThis.autoStart ??= params.autoStart !== '0';
3 |
4 | // importShim is handles the pre-patched Blazor framework scripts that have had their `import` and `export` calls changed to `importShim` and `exportShim`
5 | //import * as _importShim from "./spawndev.blazorjs.webworkers.import-shim.js"
6 | // load the event holder that holds events while Blazor loads
7 | import * as _eventHolder from "./spawndev.blazorjs.webworkers.event-holder.js"
8 | // Faux-Env creates a minimal window scope like environment (if needed) for Blazor to run in a Web Worker, Shared Worker, or Service Worker.
9 | import * as _fauxEnv from "./spawndev.blazorjs.webworkers.faux-env.js"
10 | // if the document is a fake document (faux-env) init it now
11 | if (document.initDocument) document.initDocument();
12 | // Use static imports to load Blazor pre-patched scripts
13 | // import-shim is currently pre-pended to blazor.webassembly.js when it is pre-patched.
14 | //import * as _importShim from "./spawndev.blazorjs.webworkers.import-shim.js"
15 | import * as _blazor from "./_framework/blazor.webassembly.js"
16 | import * as _dotnet from "./_framework/dotnet.js"
17 | import * as _dotnetNative from "./_framework/dotnet.native.js"
18 | import * as _dotnetRuntime from "./_framework/dotnet.runtime.js"
19 | // SpawnDev.BlazorJS Javascript library is required
20 | import * as _bjs from "./SpawnDev.BlazorJS.lib.module.js"
21 | if (globalThis.exportShimValues) globalThis.exportShimValues['SpawnDev.BlazorJS.lib.module.js'] = _bjs;
22 |
23 | var verboseWebWorkers = !!params.verbose;
24 |
25 | globalThis.consoleLog ??= function () {
26 | if (!verboseWebWorkers) return;
27 | console.log(...arguments);
28 | };
29 | // if library modules are needed, a custom loader should be created that loads those modules and then loads this file, optionally using a custom Blazor.start()
30 |
31 | // start Blazor if autoStart hasn't been disabled
32 | if (globalThis.autoStart) {
33 | Blazor._startTask = Blazor.start();
34 | }
35 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/buildcontent/patch.min.js:
--------------------------------------------------------------------------------
1 | "undefined"==typeof document&&(document=globalThis.document),"undefined"==typeof history&&(history=globalThis.history),globalThis.blazorConfig=globalThis.blazorConfig??{},globalThis.blazorConfig=Object.assign({documentBaseURI:globalThis.document?globalThis.document.baseURI:new URL("./",globalThis.location.href),blazorBaseURI:"",frameworkFolderName:"_framework",contentFolderName:"_content"},globalThis.blazorConfig),globalThis.blazorConfig.blazorBaseURI||(globalThis.blazorConfig.blazorBaseURI=function(){var o=new URL("./",location.href);if(o.pathname.includes(`${globalThis.blazorConfig.contentFolderName}/`)){var l=o.pathname.substring(0,o.pathname.indexOf(`${globalThis.blazorConfig.contentFolderName}/`));return new URL(l,location.href).toString()}if(o.pathname.includes(`${globalThis.blazorConfig.frameworkFolderName}/`)){l=o.pathname.substring(0,o.pathname.indexOf(`${globalThis.blazorConfig.frameworkFolderName}/`));return new URL(l,location.href).toString()}return o.toString()}()),void 0===globalThis.constructor.name&&globalThis.window&&(globalThis.constructor.name="Window"),"Window"!==globalThis.constructor.name&&globalThis.document&&(globalThis.document.baseURI=globalThis.blazorConfig.blazorBaseURI),globalThis.exportShimValues={},globalThis.exportShim=(o,l)=>(l?globalThis.exportShimValues[o]=l:globalThis.exportShimValues[o]||(globalThis.exportShimValues[o]={}),globalThis.exportShimValues[o]),globalThis.dynamicImportSupported="Window"===globalThis.constructor.name,globalThis.importShim=function(o){var l=o.split("?")[0].split("#")[0];return l=(l=-1===l.indexOf("/")?l:l.substring(l.lastIndexOf("/")+1)).substring(0,l.lastIndexOf(".js")+3),new Promise((async function(a,i){if(!1!==globalThis.dynamicImportSupported)try{var e=await import(o);globalThis.dynamicImportSupported=!0;var r=globalThis.exportShimValues[l]??{};return globalThis.exportShimValues[l]=Object.assign(r,e),void a(r=globalThis.exportShimValues[l])}catch(o){globalThis.dynamicImportSupported=!1}if(void 0!==globalThis.importScripts)return importScripts(o),void a(r=globalThis.exportShimValues[l]??{});i()}))},globalThis.importShim.meta=function(o){return{url:new URL(`${globalThis.blazorConfig.frameworkFolderName}/${o}`,globalThis.blazorConfig.blazorBaseURI).toString()}};
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.11.35103.136
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpawnDev.BlazorJS.WebWorkers", "SpawnDev.BlazorJS.WebWorkers\SpawnDev.BlazorJS.WebWorkers.csproj", "{6199BC07-1C75-408A-B485-25D4A483DE1A}"
7 | ProjectSection(ProjectDependencies) = postProject
8 | {A855E2C2-27D1-4494-A9CA-58EBF11DC34D} = {A855E2C2-27D1-4494-A9CA-58EBF11DC34D}
9 | EndProjectSection
10 | EndProject
11 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpawnDev.BlazorJS.WebWorkers.Build", "SpawnDev.BlazorJS.WebWorkers.Build\SpawnDev.BlazorJS.WebWorkers.Build.csproj", "{A855E2C2-27D1-4494-A9CA-58EBF11DC34D}"
12 | EndProject
13 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpawnDev.BlazorJS.WebWorkers.Demo", "SpawnDev.BlazorJS.WebWorkers.Demo\SpawnDev.BlazorJS.WebWorkers.Demo.csproj", "{BBDA1D8E-A5FF-4B17-96A0-B56D1840CBAE}"
14 | EndProject
15 | Global
16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
17 | Debug|Any CPU = Debug|Any CPU
18 | Release|Any CPU = Release|Any CPU
19 | EndGlobalSection
20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
21 | {6199BC07-1C75-408A-B485-25D4A483DE1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
22 | {6199BC07-1C75-408A-B485-25D4A483DE1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
23 | {6199BC07-1C75-408A-B485-25D4A483DE1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
24 | {6199BC07-1C75-408A-B485-25D4A483DE1A}.Release|Any CPU.Build.0 = Release|Any CPU
25 | {A855E2C2-27D1-4494-A9CA-58EBF11DC34D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26 | {A855E2C2-27D1-4494-A9CA-58EBF11DC34D}.Debug|Any CPU.Build.0 = Debug|Any CPU
27 | {A855E2C2-27D1-4494-A9CA-58EBF11DC34D}.Release|Any CPU.ActiveCfg = Release|Any CPU
28 | {A855E2C2-27D1-4494-A9CA-58EBF11DC34D}.Release|Any CPU.Build.0 = Release|Any CPU
29 | {BBDA1D8E-A5FF-4B17-96A0-B56D1840CBAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
30 | {BBDA1D8E-A5FF-4B17-96A0-B56D1840CBAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
31 | {BBDA1D8E-A5FF-4B17-96A0-B56D1840CBAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
32 | {BBDA1D8E-A5FF-4B17-96A0-B56D1840CBAE}.Release|Any CPU.Build.0 = Release|Any CPU
33 | EndGlobalSection
34 | GlobalSection(SolutionProperties) = preSolution
35 | HideSolutionNode = FALSE
36 | EndGlobalSection
37 | GlobalSection(ExtensibilityGlobals) = postSolution
38 | SolutionGuid = {B771CA8D-AD31-4F8D-ADFC-894E8DFE588B}
39 | EndGlobalSection
40 | EndGlobal
41 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.Build/Tasks/ImportShimBlazorWASM.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Build.Framework;
2 | using System;
3 | using System.IO;
4 |
5 | namespace SpawnDev.BlazorJS.WebWorkers.Build.Tasks
6 | {
7 | public class ImportShimBlazorWASM : Microsoft.Build.Utilities.Task
8 | {
9 |
10 | public string ServiceWorkerAssetsManifest { get; set; }
11 |
12 | //[Required]
13 | //public ITaskItem[] StaticWebAsset { get; set; }
14 |
15 | [Required]
16 | public string ProjectDir { get; set; }
17 |
18 | [Required]
19 | public string OutputPath { get; set; }
20 |
21 | //[Required]
22 | //public string BasePath { get; set; }
23 |
24 | [Required]
25 | public string IntermediateOutputPath { get; set; }
26 |
27 | [Required]
28 | public bool PatchFramework { get; set; }
29 |
30 | [Required]
31 | public string PackageContentDir { get; set; }
32 |
33 | [Required]
34 | public bool DebugSpawnDevWebWorkersBuildTasks { get; set; }
35 |
36 | [Required]
37 | public bool PublishMode { get; set; }
38 |
39 | public string OutputWwwroot { get; set; }
40 |
41 | public override bool Execute()
42 | {
43 | if (DebugSpawnDevWebWorkersBuildTasks)
44 | {
45 | System.Diagnostics.Debugger.Launch();
46 | }
47 | if (!PatchFramework)
48 | {
49 | return true;
50 | }
51 | OutputWwwroot = Path.GetFullPath(Path.Combine(OutputPath, "wwwroot"));
52 | PackageContentDir = Path.GetFullPath(PackageContentDir);
53 | var blazorPatchTool = new BlazorWASMFrameworkTool(OutputWwwroot, PackageContentDir, ServiceWorkerAssetsManifest);
54 | // patch Blazor _framework files to allow running in non-window scopes
55 | // this needs to run after build and after publish
56 | blazorPatchTool.ImportPatch();
57 | // if the app has an `service-worker-assets.js` file, some hashes may need to be updated due to file patching
58 | // as this file is not normally used during debugging, it is only checked during publish.
59 | // most patched files will already have the correct hash, but usually the Blazor build process overwrites 1 of them during publish
60 | // so it is patched and the hash is updated here
61 | if (PublishMode)
62 | {
63 | blazorPatchTool.VerifyAssetsManifest();
64 | }
65 | return true;
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/ExceptionSerializer.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | namespace SpawnDev.BlazorJS.WebWorkers
4 | {
5 | ///
6 | /// A simple serializer for exceptions that can be used to pass exceptions between window, service worker, dedicated worker, and shared worker contexts.
7 | ///
8 | public static class ExceptionSerializer
9 | {
10 | ///
11 | /// Exception type cache
12 | ///
13 | static Dictionary ExceptionTypes = new Dictionary();
14 | ///
15 | /// Serializes an exception to a string.
16 | ///
17 | ///
18 | ///
19 | public static string? Serialize(Exception? exception)
20 | {
21 | if (exception == null) return null;
22 | // most of the time, Exceptions prefix the Exception type's FullName to the ToString() return value but it is not required
23 | // if it is not already there, add it
24 | var exceptionString = exception.ToString();
25 | var typeNamePart = $"{exception.GetType().FullName}: ";
26 | return exceptionString.StartsWith(typeNamePart) ? exceptionString : $"{typeNamePart}{exceptionString}";
27 | }
28 | ///
29 | /// Deserializes an exception from a serialized string.
30 | ///
31 | ///
32 | ///
33 | public static Exception? Deserialize(string? serializedException)
34 | {
35 | if (string.IsNullOrEmpty(serializedException)) return null;
36 | var parts = serializedException.Split(new[] { ": " }, 2, StringSplitOptions.None);
37 | if (parts.Length < 2) return null;
38 | var typeName = parts[0];
39 | var message = parts[1];
40 | if (!ExceptionTypes.TryGetValue(typeName, out var exTypeCached))
41 | {
42 | exTypeCached = Type.GetType(typeName);
43 | ExceptionTypes[typeName] = exTypeCached;
44 | }
45 | if (exTypeCached == null)
46 | {
47 | return new Exception(serializedException);
48 | }
49 | try
50 | {
51 | return (Exception)Activator.CreateInstance(exTypeCached, new object?[] { message })!;
52 | }
53 | catch { }
54 | try
55 | {
56 | return (Exception)Activator.CreateInstance(exTypeCached)!;
57 | }
58 | catch (Exception ex)
59 | {
60 | ExceptionTypes[typeName] = null;
61 | return new Exception(serializedException);
62 | }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.Demo/Pages/TestModuleWorker.razor:
--------------------------------------------------------------------------------
1 | @page "/TestModuleWorker"
2 | @using SpawnDev.BlazorJS.JSObjects
3 | @implements IDisposable
4 |
5 | Module Worker Test
6 |
7 |
Module Worker Test
8 |
9 | This component tests starting a web worker in module mode.
10 | Some Javascript libraries require module mode, but service workers must be either 'classic' mode and use importScripts or 'module' mode and use static imports.
11 | Window, SharedWebWorker, and DedicatedWebWorker can use dynamic imports but that is not supported in a ServiceWorker.
12 | SpawnDev.BlazorJS.WebWorkers needs to support starting in a ServiceWorker that is running in 'module' mode to enable support for ESM Javascript modules.
13 | Running in a Worker that is started in module mode is very similar to running in a ServiceWorker that is started in module mode, but is easier to test.
14 |
8 | Open this page in another window. You will see that the new window will have the same counter value as this window.
9 | Clicking the button on either window will increment the counter on all windows.
10 |
11 |
12 | This demonstrates calling a static method in another window inside a component.
13 |
14 |
15 |
Current count: @currentCount
16 |
17 |
18 |
19 |
20 |
21 | @code {
22 | [Inject]
23 | WebWorkerService WebWorkerService { get; set; } = default!;
24 |
25 | private int currentCount = 0;
26 | // holds the running instance of this component, if one
27 | static Counter? instance = null;
28 |
29 | async Task OpenNewWindow()
30 | {
31 | var window = await WebWorkerService.OpenWindow();
32 | var nmt = true;
33 | }
34 |
35 | // this static method can be called by other running instances
36 | static void SetInstanceCount(int count)
37 | {
38 | if (instance == null) return;
39 | instance.currentCount = count;
40 | instance.StateHasChanged();
41 | Console.WriteLine("Count set by another window");
42 | }
43 | // this static method can be called by other running instances
44 | static int? GetInstanceCount()
45 | {
46 | return instance?.currentCount;
47 | }
48 | protected override async Task OnInitializedAsync()
49 | {
50 | // set the current count to the largest value in any other running window
51 | var windows = WebWorkerService.Windows.Where(o => o.Info.InstanceId != WebWorkerService.InstanceId).ToList();
52 | Console.WriteLine($"init Windows found: {windows.Count}");
53 | var values = new List();
54 | var tasks = windows.Select(async o =>
55 | {
56 | try
57 | {
58 | var instanceCurrentCount = await o.Run(() => GetInstanceCount()).WaitAsync(TimeSpan.FromSeconds(2));
59 | if (instanceCurrentCount != null) values.Add(instanceCurrentCount.Value);
60 | }
61 | catch { }
62 | }).ToList();
63 | await Task.WhenAll(tasks);
64 | Console.WriteLine($"Values found: {values.Count}");
65 | currentCount = values.Any() ? values.Max() : 0;
66 | Console.WriteLine($"Max value found: {currentCount}");
67 | instance = this;
68 | StateHasChanged();
69 | }
70 | public void Dispose()
71 | {
72 | // unset this instance on the static var
73 | instance = null;
74 | }
75 | private async Task IncrementCount()
76 | {
77 | // increment for this window
78 | currentCount++;
79 | // set the value on all other windows
80 | var windows = WebWorkerService.Windows.Where(o => o.Info.InstanceId != WebWorkerService.InstanceId).ToList();
81 | Console.WriteLine($"increment Windows found: {windows.Count}");
82 | var tasks = windows.Select(o => o.Run(() => SetInstanceCount(currentCount)));
83 | try
84 | {
85 | await Task.WhenAll(tasks).WaitAsync(TimeSpan.FromSeconds(3));
86 | }
87 | catch { }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/AppInstanceInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace SpawnDev.BlazorJS.WebWorkers
4 | {
5 | /////
6 | ///// Configuration data with information a new instance will use on startup
7 | /////
8 | //public class InstanceConfiguration
9 | //{
10 | // ///
11 | // /// The configuration id of this instance
12 | // ///
13 | // public string ConfigId { get; set; }
14 | // ///
15 | // /// The InstanceId of the instance that created this config
16 | // ///
17 | // public string OwnerId { get; set; }
18 | // ///
19 | // /// When this config was created
20 | // ///
21 | // public DateTimeOffset Created { get; set; }
22 | // ///
23 | // /// The path to load
24 | // ///
25 | // public string? Url { get; set; }
26 | //}
27 | ///
28 | /// Information about an instance of a Blazor app
29 | ///
30 | public class AppInstanceInfo
31 | {
32 | ///
33 | /// The instance's instanceId, a unique and randomly generated Guid string created during BlazorJSRuntime startup
34 | ///
35 | public string InstanceId { get; set; }
36 | ///
37 | /// The InstanceId of the instance that created this instance (if one)
38 | ///
39 | public string? OwnerId { get; set; }
40 | ///
41 | /// The id set by the owner (OwnerId) when they created this instance
42 | ///
43 | public string? ChildId { get; set; }
44 | ///
45 | /// The instance's location at startup
46 | ///
47 | public string Url { get; set; }
48 | ///
49 | /// The instance's app baseUri
50 | ///
51 | public string BaseUrl { get; set; }
52 | ///
53 | /// The scope the instance is running in
54 | ///
55 | public GlobalScope Scope { get; set; }
56 | ///
57 | /// The name property of the global scope. In shared workers, this is the shared worker name used when there were created.
58 | ///
59 | ///
60 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
61 | public string? Name { get; set; }
62 | ///
63 | /// If the scope is a dedicated worker, this is the parent's instance id
64 | /// If this is a window scope and running in an iframe, this is the parent window's instanceId
65 | ///
66 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
67 | public string? ParentInstanceId { get; set; }
68 | ///
69 | /// The instance's clientId. null if a undetermined.
70 | ///
71 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
72 | public string? ClientId { get; set; }
73 | ///
74 | /// The name of the instance's running indicator lock (if one)
75 | ///
76 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
77 | public string? LockName { get; set; }
78 | ///
79 | /// Returns true if this instance is a TaaskPool worker
80 | ///
81 | public bool TaskPoolWorker { get; set; }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/wwwroot/spawndev.blazorjs.webworkers.event-holder.js:
--------------------------------------------------------------------------------
1 | // Holds events that happen while Blazor WASM is loading
2 |
3 | (function () {
4 | var globalThisTypeName = globalThis.constructor?.name;
5 | if (globalThisTypeName == 'SharedWorkerGlobalScope') {
6 | // important for SharedWorker
7 | // catch any incoming connections that happen while .Net is loading
8 | let _missedConnections = [];
9 | globalThis.takeOverOnConnectEvent = function (newConnectFunction) {
10 | var tmp = _missedConnections;
11 | _missedConnections = [];
12 | globalThis.onconnect = newConnectFunction;
13 | return tmp;
14 | }
15 | globalThis.onconnect = function (e) {
16 | _missedConnections.push(e.ports[0]);
17 | };
18 | } else if (globalThisTypeName == 'ServiceWorkerGlobalScope') {
19 | // Starting Blazor requires using importScripts inside async functions
20 | // e.waitUntil is used during the install event to allow importScripts inside async functions
21 | // it is resolved after loading is complete
22 | let holdEvents = true;
23 | let missedServiceWorkerEvents = [];
24 | function handleMissedEvent(e) {
25 | if (!holdEvents) return;
26 | consoleLog('ServiceWorker missed event:', e.type, e);
27 | if (e.respondWith) {
28 | // fetch and canmakepayment ExtendableEvents use respondWith
29 | var responsePromise = new Promise(function (resolve, reject) {
30 | e.responseResolve = resolve;
31 | e.responseReject = reject;
32 | });
33 | e.respondWith(responsePromise);
34 | } else if (e.waitUntil) {
35 | // all other ExtendableEvents use waitUntil
36 | var waitUntilPromise = new Promise(function (resolve, reject) {
37 | e.waitResolve = resolve;
38 | e.waitReject = reject;
39 | });
40 | e.waitUntil(waitUntilPromise);
41 | }
42 | missedServiceWorkerEvents.push(e);
43 | }
44 | globalThis.addEventListener('activate', handleMissedEvent);
45 | globalThis.addEventListener('backgroundfetchabort', handleMissedEvent);
46 | globalThis.addEventListener('backgroundfetchclick', handleMissedEvent);
47 | globalThis.addEventListener('backgroundfetchfail', handleMissedEvent);
48 | globalThis.addEventListener('backgroundfetchsuccess', handleMissedEvent);
49 | globalThis.addEventListener('canmakepayment', handleMissedEvent);
50 | globalThis.addEventListener('contentdelete', handleMissedEvent);
51 | globalThis.addEventListener('cookiechange', handleMissedEvent);
52 | globalThis.addEventListener('fetch', handleMissedEvent);
53 | globalThis.addEventListener('install', handleMissedEvent);
54 | globalThis.addEventListener('message', handleMissedEvent);
55 | globalThis.addEventListener('messageerror', handleMissedEvent);
56 | globalThis.addEventListener('notificationclick', handleMissedEvent);
57 | globalThis.addEventListener('notificationclose', handleMissedEvent);
58 | globalThis.addEventListener('paymentrequest', handleMissedEvent);
59 | globalThis.addEventListener('periodicsync', handleMissedEvent);
60 | globalThis.addEventListener('push', handleMissedEvent);
61 | globalThis.addEventListener('pushsubscriptionchange', handleMissedEvent);
62 | globalThis.addEventListener('sync', handleMissedEvent);
63 | // This method will be called by Blazor WASM when it starts up to collect missed events and handle them
64 | globalThis.GetMissedServiceWorkerEvents = function () {
65 | holdEvents = false;
66 | var ret = missedServiceWorkerEvents;
67 | missedServiceWorkerEvents = [];
68 | return ret;
69 | };
70 | }
71 | })()
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/SharedCancellationToken.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace SpawnDev.BlazorJS.WebWorkers
5 | {
6 | ///
7 | /// SharedCancellationToken is a class that uses a SharedArrayBuffer to allow checking if a task has been cancelled by another browser thread
8 | /// synchronously and without relying on message event handling.
9 | ///
10 | [JsonConverter(typeof(JsonConverters.HybridObjectConverterFactory))]
11 | public class SharedCancellationToken : IDisposable
12 | {
13 | // JsonInclude on non-public properties is supported by HybridObjectConverter
14 | [JsonInclude]
15 | [JsonPropertyName("cancelled")]
16 | private bool _cancelled { get; set; } = false;
17 |
18 | // JsonInclude on non-public properties is supported by HybridObjectConverter
19 | [JsonInclude]
20 | [JsonPropertyName("source")]
21 | private SharedCancellationTokenSource? _source { get; set; } = null;
22 | internal SharedCancellationToken(SharedCancellationTokenSource source)
23 | {
24 | _source = source;
25 | }
26 | // json constructor (HybridObjectConverter will use this because it is marked JsonConstructorAttribute)
27 | [JsonConstructor]
28 | private SharedCancellationToken() { }
29 | ///
30 | /// Creates an instance of SharedCancellationToken and setting the cancelled state that cannot be cancelled in the future
31 | ///
32 | public SharedCancellationToken(bool cancelled = false)
33 | {
34 | _cancelled = cancelled;
35 | }
36 | ///
37 | /// Returns an instance of SharedCancellationToken that is not cancelled and will never be in the cancelled state
38 | ///
39 | public static SharedCancellationToken None => new SharedCancellationToken(false);
40 | ///
41 | /// Returns an instance of SharedCancellationToken that is cancelled.
42 | ///
43 | public static SharedCancellationToken Cancelled => new SharedCancellationToken(true);
44 | ///
45 | /// Throws an OperationCanceledException if the cancelled flag is set to true
46 | ///
47 | ///
48 | public void ThrowIfCancellationRequested()
49 | {
50 | if (IsCancellationRequested) ThrowOperationCanceledException();
51 | }
52 | // Throws an OCE; separated out to enable better inlining of ThrowIfCancellationRequested
53 | [DoesNotReturn]
54 | private void ThrowOperationCanceledException() => throw new OperationCanceledException();
55 | ///
56 | /// Returns true if the cancelled flag is set to true
57 | ///
58 | [JsonIgnore]
59 | public bool IsCancellationRequested
60 | {
61 | get
62 | {
63 | if (_cancelled) return true;
64 | if (_source != null)
65 | {
66 | // update local _cancelled flag from _source
67 | _cancelled = _source.IsCancellationRequested;
68 | }
69 | return _cancelled;
70 | }
71 | }
72 | ///
73 | /// Returns true of this SharedCancellationToken can be cancelled
74 | ///
75 | public bool CanBeCanceled => _source != null;
76 | ///
77 | /// Returns true if this instance has been disposed
78 | ///
79 | public bool IsDisposed { get; private set; } = false;
80 | ///
81 | /// Releases disposable resources
82 | ///
83 | public void Dispose()
84 | {
85 | if (IsDisposed) return;
86 | IsDisposed = true;
87 | if (_source != null)
88 | {
89 | _source.Dispose();
90 | _source = null;
91 | }
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers.Demo/wwwroot/css/app.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
3 | }
4 |
5 | h1:focus {
6 | outline: none;
7 | }
8 |
9 | a, .btn-link {
10 | color: #0071c1;
11 | }
12 |
13 | .btn-primary {
14 | color: #fff;
15 | background-color: #1b6ec2;
16 | border-color: #1861ac;
17 | }
18 |
19 | .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
20 | box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
21 | }
22 |
23 | .content {
24 | padding-top: 1.1rem;
25 | }
26 |
27 | .valid.modified:not([type=checkbox]) {
28 | outline: 1px solid #26b050;
29 | }
30 |
31 | .invalid {
32 | outline: 1px solid red;
33 | }
34 |
35 | .validation-message {
36 | color: red;
37 | }
38 |
39 | #blazor-error-ui {
40 | background: lightyellow;
41 | bottom: 0;
42 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
43 | display: none;
44 | left: 0;
45 | padding: 0.6rem 1.25rem 0.7rem 1.25rem;
46 | position: fixed;
47 | width: 100%;
48 | z-index: 1000;
49 | }
50 |
51 | #blazor-error-ui .dismiss {
52 | cursor: pointer;
53 | position: absolute;
54 | right: 0.75rem;
55 | top: 0.5rem;
56 | }
57 |
58 | .blazor-error-boundary {
59 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
60 | padding: 1rem 1rem 1rem 3.7rem;
61 | color: white;
62 | }
63 |
64 | .blazor-error-boundary::after {
65 | content: "An error has occurred."
66 | }
67 |
68 | .loading-progress {
69 | position: relative;
70 | display: block;
71 | width: 8rem;
72 | height: 8rem;
73 | margin: 20vh auto 1rem auto;
74 | }
75 |
76 | .loading-progress circle {
77 | fill: none;
78 | stroke: #e0e0e0;
79 | stroke-width: 0.6rem;
80 | transform-origin: 50% 50%;
81 | transform: rotate(-90deg);
82 | }
83 |
84 | .loading-progress circle:last-child {
85 | stroke: #1b6ec2;
86 | stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
87 | transition: stroke-dasharray 0.05s ease-in-out;
88 | }
89 |
90 | .loading-progress-text {
91 | position: absolute;
92 | text-align: center;
93 | font-weight: bold;
94 | inset: calc(20vh + 3.25rem) 0 auto 0.2rem;
95 | }
96 |
97 | .loading-progress-text:after {
98 | content: var(--blazor-load-percentage-text, "Loading");
99 | }
100 |
101 | code {
102 | color: #c02d76;
103 | }
104 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/WorkerCancellationToken.cs:
--------------------------------------------------------------------------------
1 | //using System.Diagnostics.CodeAnalysis;
2 | //using System.Text.Json.Serialization;
3 |
4 | //namespace SpawnDev.BlazorJS.WebWorkers
5 | //{
6 | // public class WorkerCancellationToken
7 | // {
8 | // [JsonInclude]
9 | // [JsonPropertyName("cancelled")]
10 | // private bool _cancelled = false;
11 | // [JsonInclude]
12 | // [JsonPropertyName("cancellationId")]
13 | // internal int CancellationId { get; private set; }
14 | // internal Func? _cancelledCheck { get; set; }
15 | // internal Action? _releaseCallback { get; set; }
16 | // internal string SourceInstanceId { get; set; }
17 | // internal WorkerCancellationToken(Func cancelledCheck, Action releaseCallback, int cancellationId)
18 | // {
19 | // _cancelledCheck = cancelledCheck;
20 | // _releaseCallback = releaseCallback;
21 | // CancellationId = cancellationId;
22 | // }
23 | // ///
24 | // /// Creates a new instance and sets the cancelled state
25 | // ///
26 | // ///
27 | // public WorkerCancellationToken(bool cancelled)
28 | // {
29 | // _cancelled = cancelled;
30 | // }
31 | // ///
32 | // /// Creates a new instance
33 | // ///
34 | // public WorkerCancellationToken() { }
35 | // ///
36 | // /// Returns an instance of SharedCancellationToken that is not cancelled and will never be in the cancelled state
37 | // ///
38 | // public static WorkerCancellationToken None => new WorkerCancellationToken(false);
39 | // ///
40 | // /// Returns an instance of SharedCancellationToken that is cancelled.
41 | // ///
42 | // public static WorkerCancellationToken Cancelled => new WorkerCancellationToken(true);
43 | // ///
44 | // /// Throws an OperationCanceledException if the cancelled flag is set to true
45 | // ///
46 | // ///
47 | // public void ThrowIfCancellationRequested()
48 | // {
49 | // if (IsCancellationRequested) ThrowOperationCanceledException();
50 | // }
51 | // // Throws an OCE; separated out to enable better inlining of ThrowIfCancellationRequested
52 | // [DoesNotReturn]
53 | // private void ThrowOperationCanceledException() => throw new OperationCanceledException();
54 | // ///
55 | // /// Returns true if the cancelled flag is set to true
56 | // ///
57 | // [JsonIgnore]
58 | // public bool IsCancellationRequested
59 | // {
60 | // get
61 | // {
62 | // if (_cancelled) return true;
63 | // if (_cancelledCheck != null)
64 | // {
65 | // // update local _cancelled flag from _source
66 | // // the below call may
67 | // _cancelled = _cancelledCheck(this);
68 | // }
69 | // return _cancelled;
70 | // }
71 | // }
72 | // ///
73 | // /// Returns true of this SharedCancellationToken can be cancelled
74 | // ///
75 | // [JsonIgnore]
76 | // public bool CanBeCanceled => _cancelledCheck != null;
77 | // ///
78 | // /// Returns true if this instance has been disposed
79 | // ///
80 | // [JsonIgnore]
81 | // public bool IsDisposed { get; private set; } = false;
82 | // ///
83 | // /// Releases disposable resources
84 | // ///
85 | // public void Dispose()
86 | // {
87 | // if (IsDisposed) return;
88 | // IsDisposed = true;
89 | // if (_releaseCallback != null)
90 | // {
91 | // _releaseCallback(this);
92 | // }
93 | // }
94 | // }
95 | //}
96 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/IServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace SpawnDev.BlazorJS.WebWorkers
4 | {
5 | ///
6 | /// Adds SpawnDev.BlazorJS.WebWorkers specific methods to IServiceCollection
7 | ///
8 | public static class IServiceCollectionExtensions
9 | {
10 | static internal Type? ServiceWorkerEventHandlerTService { get; set; } = null;
11 | static internal Type? ServiceWorkerEventHandlerTImplementation { get; set; } = null;
12 | static internal ServiceWorkerConfig? ServiceWorkerConfig { get; set; } = null;
13 | ///
14 | /// Adds WebWorkerService as a singleton service
15 | ///
16 | ///
17 | ///
18 | ///
19 | public static IServiceCollection AddWebWorkerService(this IServiceCollection _this, Action? configureCallback = null)
20 | {
21 | return _this.AddSingleton(sp =>
22 | {
23 | // create the service and pass it to the configureCallback to allow configuration at startup
24 | var webWorkerService = ActivatorUtilities.CreateInstance(sp);
25 | configureCallback?.Invoke(webWorkerService);
26 | return webWorkerService;
27 | });
28 | }
29 |
30 | ///
31 | /// RegisterServiceWorker a class that implements ServiceWorkerEventHandler to handle ServiceWorker events
32 | ///
33 | ///
34 | ///
35 | ///
36 | ///
37 | public static IServiceCollection RegisterServiceWorker(this IServiceCollection _this, ServiceWorkerConfig? config = null) where TService : ServiceWorkerEventHandler
38 | {
39 | ServiceWorkerConfig = config ?? new ServiceWorkerConfig { Register = ServiceWorkerStartupRegistration.Register };
40 | var typeTService = typeof(TService);
41 | ServiceWorkerEventHandlerTService = typeTService;
42 | ServiceWorkerEventHandlerTImplementation = typeTService;
43 | _this.AddSingleton(GlobalScope.ServiceWorker);
44 | _this.AddSingleton(sp => sp.GetRequiredService(), GlobalScope.ServiceWorker);
45 | return _this;
46 | }
47 | ///
48 | /// RegisterServiceWorker a class that implements ServiceWorkerEventHandler to handle ServiceWorker events
49 | ///
50 | ///
51 | ///
52 | ///
53 | ///
54 | ///
55 | public static IServiceCollection RegisterServiceWorker(this IServiceCollection _this, GlobalScope startScope, ServiceWorkerConfig? config = null) where TService : ServiceWorkerEventHandler
56 | {
57 | ServiceWorkerConfig = config ?? new ServiceWorkerConfig { Register = ServiceWorkerStartupRegistration.Register };
58 | var typeTService = typeof(TService);
59 | ServiceWorkerEventHandlerTService = typeTService;
60 | ServiceWorkerEventHandlerTImplementation = typeTService;
61 | _this.AddSingleton(GlobalScope.ServiceWorker | startScope);
62 | _this.AddSingleton(sp => sp.GetRequiredService(), GlobalScope.ServiceWorker);
63 | return _this;
64 | }
65 | ///
66 | /// If a ServiceWorker is no longer desired, it can be set to unregister.
67 | ///
68 | ///
69 | ///
70 | public static IServiceCollection UnregisterServiceWorker(this IServiceCollection _this)
71 | {
72 | ServiceWorkerConfig = new ServiceWorkerConfig { Register = ServiceWorkerStartupRegistration.Unregister };
73 | return _this;
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/CancellationTokenExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 |
3 | namespace SpawnDev.BlazorJS.WebWorkers
4 | {
5 | public static class CancellationTokenExtensions
6 | {
7 | static Stopwatch? TokenTaskDelayStopSwatch { get; set; }
8 | ///
9 | /// Releases the thread to other async Tasks via a Task.Delay() call and then returns CancellationToken.IsCancellationRequested
10 | /// This can allow event handlers time to process events that may cancel the token
11 | ///
12 | ///
13 | ///
14 | ///
15 | public static async Task IsCancellationRequestedAsync(this CancellationToken _this, int millisecondsDelay = 1)
16 | {
17 | await Task.Delay(millisecondsDelay);
18 | return _this.IsCancellationRequested;
19 | }
20 | ///
21 | /// Releases the thread to other async Tasks via a Task.Delay() call and then calls CancellationToken.ThrowIfCancellationRequested()
22 | /// This can allow event handlers time to process events that may cancel the token
23 | ///
24 | ///
25 | ///
26 | ///
27 | public static async Task ThrowIfCancellationRequestedAsync(this CancellationToken _this, int millisecondsDelay = 1)
28 | {
29 | await Task.Delay(millisecondsDelay);
30 | _this.ThrowIfCancellationRequested();
31 | }
32 | ///
33 | /// Releases the thread to other async Tasks via a Task.Delay() call and then returns CancellationToken.IsCancellationRequested
34 | /// This can allow event handlers time to process events that may cancel the token
35 | ///
36 | ///
37 | /// Minimum time that needs to have passed before calling Task.Delay again
38 | ///
39 | /// Value passed to Task.Delay(), which allows other async Tasks time to run.
40 | /// Too small a value will not allow time for waiting Tasks and events a chance to start. ~1 millisecond is recommended.
41 | ///
42 | ///
43 | public static async Task IsCancellationRequestedAsync(this CancellationToken _this, double minIntervalMilliseconds, int millisecondsDelay = 1)
44 | {
45 | if (TokenTaskDelayStopSwatch == null)
46 | {
47 | TokenTaskDelayStopSwatch = Stopwatch.StartNew();
48 | await Task.Delay(millisecondsDelay);
49 | }
50 | else if (TokenTaskDelayStopSwatch.Elapsed.TotalMilliseconds > minIntervalMilliseconds)
51 | {
52 | TokenTaskDelayStopSwatch.Restart();
53 | await Task.Delay(millisecondsDelay);
54 | }
55 | return _this.IsCancellationRequested;
56 | }
57 | ///
58 | /// Releases the thread to other async Tasks via a Task.Delay() call and then calls CancellationToken.ThrowIfCancellationRequested()
59 | /// This can allow event handlers time to process events that may cancel the token
60 | ///
61 | ///
62 | /// Minimum time that needs to have passed before calling Task.Delay again
63 | ///
64 | /// Value passed to Task.Delay(), which allows other async Tasks time to run.
65 | /// Too small a value will not allow time for waiting Tasks and events a chance to start. ~1 millisecond is recommended.
66 | ///
67 | ///
68 | public static async Task ThrowIfCancellationRequestedAsync(this CancellationToken _this, double minIntervalMilliseconds, int millisecondsDelay = 1)
69 | {
70 | if (TokenTaskDelayStopSwatch == null)
71 | {
72 | TokenTaskDelayStopSwatch = Stopwatch.StartNew();
73 | await Task.Delay(millisecondsDelay);
74 | }
75 | else if (TokenTaskDelayStopSwatch.Elapsed.TotalMilliseconds > minIntervalMilliseconds)
76 | {
77 | TokenTaskDelayStopSwatch.Restart();
78 | await Task.Delay(millisecondsDelay);
79 | }
80 | _this.ThrowIfCancellationRequested();
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/build/SpawnDev.BlazorJS.WebWorkers.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <_WebWorkerBuildContentDirectory>$(MSBuildThisFileDirectory)..\buildcontent
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | <_WebWorkerBuild_Project_Assets_Directory>$(ProjectDir)$(WebWorkerBuildAssetsPath)
21 | <_WebWorkerBuild_Project_Assets_ManifestJson_FilePath>$(_WebWorkerBuild_Project_Assets_Directory)\manifest.json
22 | <_WebWorkerBuild_Project_BuildOutput_Assets_Directory>$(TargetDir)$(WebWorkerBuildAssetsPath)
23 | <_WebWorkerBuild_Project_BuildOutput_WebWorkerBuild_Directory>$(TargetDir)$(WebWorkerBuildOutputPath)
24 | <_WebWorkerBuild_Project_BuildOutput_WebWorkerBuild_OriginalFramework_Directory>$(_WebWorkerBuild_Project_BuildOutput_WebWorkerBuild_Directory)\_framework
25 |
31 | <_WebWorkerBuild_Project_BuildOutput_StaticWebAssets_Manifest_FilePath>$(TargetDir)$(ProjectName).staticwebassets.runtime.json
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/SpawnDev.BlazorJS.WebWorkers.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0;net7.0;net8.0;net9.0;net10.0
5 | enable
6 | enable
7 | 2.26.0
8 | True
9 | true
10 | true
11 | Embedded
12 | SpawnDev.BlazorJS.WebWorkers
13 | LostBeard
14 | Call Services and static methods in separate threads with WebWorkers and SharedWebWorkers. Run Blazor WASM in the ServiceWorker.
15 | https://github.com/LostBeard/SpawnDev.BlazorJS.WebWorkers
16 | README.md
17 | LICENSE.txt
18 | icon-128.png
19 | https://github.com/LostBeard/SpawnDev.BlazorJS.WebWorkers.git
20 | git
21 | Blazor;BlazorWebAssembly;MultiThreading;ServiceWorker;WebWorker
22 | true
23 | true
24 | true
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | true
45 | build
46 |
47 |
48 | true
49 | buildcontent
50 |
51 |
52 | true
53 | tasks
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | /
67 |
68 |
69 |
70 |
74 | false
75 | false
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/SpawnDev.BlazorJS.WebWorkers/InterfaceCallDispatcher.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | namespace SpawnDev.BlazorJS.WebWorkers
4 | {
5 | ///
6 | /// This Proxy presents itself as the interface it is created with but calls are converted to MethodInfos with arguments and passed onto the ICallDispatcher given at creation
7 | ///
8 | ///
9 | public class InterfaceCallDispatcher : DispatchProxy where TServiceInterface : class
10 | {
11 | private Func? Resolver { get; set; }
12 | private Func>? AsyncResolver { get; set; }
13 | private Func? KeyedResolver { get; set; }
14 | private Func>? AsyncKeyedResolver { get; set; }
15 | ///
16 | /// Service Key
17 | ///
18 | public object? Key { get; private set; }
19 | ///
20 | /// True if the the service is a keyed service
21 | ///
22 | public bool Keyed { get; private set; }
23 | ///
24 | /// The service Type
25 | ///
26 | public Type ServiceType { get; private set; } = default!;
27 | private Task