diff --git a/SAFE.Meta.sln b/SAFE.Meta.sln
index 158638a..3041cec 100644
--- a/SAFE.Meta.sln
+++ b/SAFE.Meta.sln
@@ -11,9 +11,9 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "SAFE.Server", "src\SAFE.Ser
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Build", "Build\Build.fsproj", "{F49104E3-DBA9-4A2D-B839-B4E60BCAE698}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{387BFC82-96EA-4C10-A08E-C25588EE2223}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{30BCECCE-E11D-4028-A9DE-6A83DF2E3EEC}"
EndProject
-Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "SAFE.Client.Tests", "test\SAFE.Client.Tests\SAFE.Client.Tests.fsproj", "{ECC5C643-1340-4718-8381-E3D1E835C3E3}"
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "SAFE.Client.Tests", "test\SAFE.Client.Tests\SAFE.Client.Tests.fsproj", "{66BAE342-23E0-4904-B98E-B0679D9370D7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -36,13 +36,14 @@ Global
{F49104E3-DBA9-4A2D-B839-B4E60BCAE698}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F49104E3-DBA9-4A2D-B839-B4E60BCAE698}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F49104E3-DBA9-4A2D-B839-B4E60BCAE698}.Release|Any CPU.Build.0 = Release|Any CPU
- {ECC5C643-1340-4718-8381-E3D1E835C3E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {ECC5C643-1340-4718-8381-E3D1E835C3E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {ECC5C643-1340-4718-8381-E3D1E835C3E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {ECC5C643-1340-4718-8381-E3D1E835C3E3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {66BAE342-23E0-4904-B98E-B0679D9370D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {66BAE342-23E0-4904-B98E-B0679D9370D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {66BAE342-23E0-4904-B98E-B0679D9370D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {66BAE342-23E0-4904-B98E-B0679D9370D7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{5BCFE180-027E-40F9-847B-7B36F10BD5D7} = {59161B7D-F517-4F4B-B28D-33E34B8A930E}
{0C8F62AC-D5D8-43E6-A4C7-159E530C3CE5} = {59161B7D-F517-4F4B-B28D-33E34B8A930E}
+ {66BAE342-23E0-4904-B98E-B0679D9370D7} = {30BCECCE-E11D-4028-A9DE-6A83DF2E3EEC}
EndGlobalSection
-EndGlobal
\ No newline at end of file
+EndGlobal
diff --git a/paket.dependencies b/paket.dependencies
index 727fbb0..0f20546 100644
--- a/paket.dependencies
+++ b/paket.dependencies
@@ -7,6 +7,8 @@ nuget Fake.JavaScript.Npm
nuget FSharp.Core ~> 8
nuget Fable.Remoting.Giraffe ~> 5
+nuget SAFE.Client.Utils ~> 5
+nuget SAFE.Server.Utils ~> 5
nuget Saturn ~> 0
nuget Fable.Core ~> 4
diff --git a/paket.lock b/paket.lock
index a76f7cd..232f3ef 100644
--- a/paket.lock
+++ b/paket.lock
@@ -163,6 +163,13 @@ NUGET
Microsoft.IdentityModel.Logging (>= 7.7.1)
Microsoft.IO.RecyclableMemoryStream (3.0.1)
Newtonsoft.Json (13.0.3)
+ SAFE.Client.Utils (5.0.1)
+ Fable.Browser.Dom (>= 2.18.1)
+ Fable.Remoting.Client (>= 7.32)
+ Fable.SimpleJson (>= 3.24)
+ FSharp.Core (>= 8.0.403 < 9.0)
+ SAFE.Server.Utils (5.0)
+ Fable.Remoting.Giraffe (>= 5.21)
Saturn (0.17)
FSharp.Control.Websockets (>= 0.2.2)
Giraffe (>= 6.4)
@@ -172,5 +179,4 @@ NUGET
Microsoft.IdentityModel.JsonWebTokens (>= 7.7.1)
Microsoft.IdentityModel.Tokens (>= 7.7.1)
System.Reactive (5.0)
- System.Text.Encodings.Web (8.0)
System.Text.Json (8.0.5)
diff --git a/readme.md b/readme.md
index 58f01c6..856d807 100644
--- a/readme.md
+++ b/readme.md
@@ -1,7 +1,7 @@
This repository contains the two metapackages used by the SAFE Template.
## SAFE.Server
-Contains dependencies required for the SAFE Stack server component, as well as a set of helper types and utilities.
+Contains dependencies required for the SAFE Stack server component
## SAFE.Client
-Contains dependencies required for the SAFE Stack client component, as well as a set of helper types and utilities, including `AsyncMsg` and `Deferred` types.
\ No newline at end of file
+Contains dependencies required for the SAFE Stack client component
\ No newline at end of file
diff --git a/src/SAFE.Client/SAFE.Client.fsproj b/src/SAFE.Client/SAFE.Client.fsproj
index 67a8b5b..86a9da4 100644
--- a/src/SAFE.Client/SAFE.Client.fsproj
+++ b/src/SAFE.Client/SAFE.Client.fsproj
@@ -28,7 +28,6 @@
-
diff --git a/src/SAFE.Client/SAFE.fs b/src/SAFE.Client/SAFE.fs
deleted file mode 100644
index 6fdae2e..0000000
--- a/src/SAFE.Client/SAFE.fs
+++ /dev/null
@@ -1,188 +0,0 @@
-namespace SAFE
-
-open Fable.Remoting.Client
-open Fable.SimpleJson
-open System
-
-/// Contains functionality to interact with Fable Remoting APIs.
-type Api =
- /// Quickly creates a Fable Remoting API proxy. For more details, see https://zaid-ajaj.github.io/Fable.Remoting/.
- /// An optional function which takes the name of the API and the method being called, and generates the route to the server API. Defaults to `/api/{api name}/{method name}` e.g. `/api/ITodoApi/GetTodos`.
- /// An optional function which allows you to customize the API proxy such as Binary Serialization e.g. https://zaid-ajaj.github.io/Fable.Remoting/#/advanced/binary-serialization.
- static member inline makeProxy<'TApi>(?routeBuilder, ?customOptions) =
- let routeBuilder = defaultArg routeBuilder (sprintf "/api/%s/%s")
- let customOptions = defaultArg customOptions id
-
- Remoting.createApi ()
- |> Remoting.withRouteBuilder routeBuilder
- |> customOptions
- |> Remoting.buildProxy<'TApi>
-
-[]
-module Extensions =
- type System.Exception with
-
- /// If propagating exceptions with Fable Remoting, this helper method deserializes any `ProxyRequestException` into a record that contains the exception details.
- /// See https://zaid-ajaj.github.io/Fable.Remoting/#/advanced/error-handling for more details.
- member this.GetPropagatedError() =
- match this with
- | :? ProxyRequestException as exn ->
- let response =
- exn.ResponseText
- |> Json.parseAs<
- {|
- error: {| ClassName: string; Message: string |}
- ignored: bool
- handled: bool
- |}
- >
-
- response.error
- | ex -> {|
- ClassName = "Unknown"
- Message = ex.Message
- |}
-
-/// Used commonly to model asynchronous calls to the server (or any other external service) within an Elmish message instead of two separate messages e.g. `LoadData` and `DataLoaded`. `Start` represents the initial request (command); `Finished` contains the resultant data on the callback.
-///
-/// For an example of how to use this type, see https://safe-stack.github.io/docs/recipes/client-server/mvu-roundtrip/.
-///
-/// For reference documentation, see the AsyncOperation type in https://zaid-ajaj.github.io/the-elmish-book/#/chapters/commands/async-state.
-type ApiCall<'TStart, 'TFinished> =
- /// Represents an API call that should be initiated.
- | Start of 'TStart
- /// Represents the response of an API call.
- | Finished of 'TFinished
-
-/// Typically used for data on a model that will be loaded from the server. This type represents some data which has either not yet started loading, is currently in the process of being loaded, or has been loaded and is available.
-/// See the Deferred type in https://zaid-ajaj.github.io/the-elmish-book/#/chapters/commands/async-state#conclusion for more details.
-type RemoteData<'T> =
- /// The data has not yet started loading.
- | NotStarted
- /// The data is now being loaded. May already contain some data i.e. a refresh operation is in process.
- | Loading of 'T option
- /// The data is available.
- | Loaded of 'T
-
- /// Unwraps the Loaded value, or returns the supplied default value.
- member this.DefaultValue v = this.AsOption |> Option.defaultValue v
-
- /// Returns whether the `RemoteData<'T>` value has been loaded.
- member this.HasLoaded =
- match this with
- | NotStarted
- | Loading _ -> false
- | Loaded _ -> true
-
- /// Returns whether the `RemoteData<'T>` value has started loading or not.
- member this.HasStarted =
- match this with
- | NotStarted -> false
- | Loading _
- | Loaded _ -> true
-
- /// Returns whether the `RemoteData<'T>` value has been loaded.
- member this.HasData = Option.isSome this.AsOption
-
- /// Returns whether the `RemoteData<'T>` value is loading. This will return true for both first-time and refresh-style loads.
- member this.IsStillLoading =
- match this with
- | Loading _ -> true
- | NotStarted
- | Loaded _ -> false
-
- /// Returns whether the `RemoteData<'T>` value is refreshing itself i.e. Loading (Some _)
- member this.IsRefreshing =
- match this with
- | Loading(Some _) -> true
- | _ -> false
-
- /// Returns whether the `RemoteData<'T>` value has not yet started.
- member this.HasNotStarted =
- match this with
- | NotStarted -> true
- | Loading _
- | Loaded _ -> false
-
- /// Maps the underlying value of the remote data, when it exists, into another shape.
- member this.Map mapper =
- match this with
- | NotStarted -> NotStarted
- | Loaded value -> Loaded(mapper value)
- | Loading None -> Loading None
- | Loading(Some value) -> Loading(Some(mapper value))
-
- /// Verifies that a `RemoteData<'T>` value has some data loaded (may be Loading or Loaded), and that the data satisfies a given requirement.
- member this.Exists predicate =
- this.AsOption |> Option.exists predicate
-
- /// Like `map` but instead of mapping just the value into another type in the `Loading` or `Loaded` case, it will transform the value into potentially a different case of the `RemoteData<'T>` type.
- member this.Bind binder =
- match this.AsOption with
- | Some v -> binder v
- | None -> this
-
- /// Maps `Loaded` or `Loading Some` to `Some`, everything else to `None`.
- []
- member this.ToOption() = this.AsOption
-
- /// Maps `Loaded` or `Loading Some` to `Some`, everything else to `None`.
- member this.AsOption =
- match this with
- | Loaded value
- | Loading(Some value) -> Some value
- | NotStarted
- | Loading None -> None
-
- /// Transitions to Loading, retaining existing data as needed.
- ///
- /// ```
- /// NotStarted | Loading None -> Loading None
- /// Loaded x | Loading (Some x) -> Loading (Some x)
- /// ```
- member this.StartLoading() = Loading this.AsOption
-
-/// Contains utility functions on the `Remote` type.
-module RemoteData =
- /// Maps `Loaded` to `Some`, everything else to `None`.
- let asOption (remote: RemoteData<'T>) = remote.AsOption
-
- /// Maps `Loaded` to `Some`, everything else to `None`.
- []
- let toOption = asOption
-
- /// Unwraps the Loaded value, or returns the default value.
- let defaultValue defaultValue (remote: RemoteData<'T>) = remote.DefaultValue defaultValue
-
- /// Returns whether the `RemoteData<'T>` value has been loaded.
- let hasLoaded (remote: RemoteData<'T>) = remote.HasLoaded
-
- /// Returns whether the `RemoteData<'T>` value has started loading or not.
- let hasStarted (remote: RemoteData<'T>) = remote.HasStarted
-
- /// Returns whether the `RemoteData<'T>` value has started loading.
- let hasNotStarted (remote: RemoteData<'T>) = remote.HasNotStarted
-
- /// Returns whether the `RemoteData<'T>` value is loading.
- let isLoading (remote: RemoteData<_>) = remote.IsStillLoading
-
- /// Returns whether the `RemoteData<'T>` value is refreshing.
- let isRefreshing (remote: RemoteData<_>) = remote.IsRefreshing
-
- /// Returns whether the `RemoteData<'T>` value has data.
- let hasData (remote: RemoteData<_>) = remote.HasData
-
- /// Maps the underlying value of the remote data, when it exists, into another shape
- let map mapper (remote: RemoteData<'T>) = remote.Map mapper
-
- /// Verifies that a `RemoteData<'T>` value is loaded, and that the data satisfies a given requirement.
- let exists predicate (remote: RemoteData<'T>) = remote.Exists predicate
-
- /// Like `map` but instead of mapping just the value into another type in the `Loaded` case, it will transform the value into potentially a different case of the `RemoteData<'T>` type.
- let bind binder (remote: RemoteData<'T>) = remote.Bind binder
-
- /// Transitions to Loading, retaining existing data as needed.
- /// `Loaded x -> Loading x`;
- /// `NotStarted -> Loading None`;
- /// `Loading x -> Loading x`;
- let startLoading (remote: RemoteData<'T>) = remote.StartLoading
\ No newline at end of file
diff --git a/src/SAFE.Client/paket.references b/src/SAFE.Client/paket.references
index 999f7ca..56ac6a0 100644
--- a/src/SAFE.Client/paket.references
+++ b/src/SAFE.Client/paket.references
@@ -6,4 +6,5 @@ Fable.Elmish.HMR
Fable.Mocha
Fable.Remoting.Client
Feliz
-Fable.SimpleJson
\ No newline at end of file
+Fable.SimpleJson
+SAFE.Client.Utils
\ No newline at end of file
diff --git a/src/SAFE.Client/readme.md b/src/SAFE.Client/readme.md
index 30faefb..d95ee8e 100644
--- a/src/SAFE.Client/readme.md
+++ b/src/SAFE.Client/readme.md
@@ -1 +1 @@
-Contains dependencies required for the SAFE Stack client component, as well as a set of helper types and utilities, including `AsyncMsg` and `Deferred` types.
\ No newline at end of file
+Contains dependencies required for the SAFE Stack client component
\ No newline at end of file
diff --git a/src/SAFE.Server/SAFE.Server.fsproj b/src/SAFE.Server/SAFE.Server.fsproj
index 4fd8e31..d5cb625 100644
--- a/src/SAFE.Server/SAFE.Server.fsproj
+++ b/src/SAFE.Server/SAFE.Server.fsproj
@@ -1,4 +1,5 @@
-
+
+
SAFE
@@ -26,8 +27,5 @@
-
-
-
diff --git a/src/SAFE.Server/SAFE.fs b/src/SAFE.Server/SAFE.fs
deleted file mode 100644
index 3010d64..0000000
--- a/src/SAFE.Server/SAFE.fs
+++ /dev/null
@@ -1,52 +0,0 @@
-namespace SAFE
-
-open Giraffe
-open Fable.Remoting.Server
-open Fable.Remoting.Giraffe
-
-[]
-module Constants =
- open Microsoft.Extensions.Hosting
-
- /// Tests if the current environment is development based on the presence of the ASPNETCORE_ENVIRONMENT envionment variable.
- let isDev =
- System.Environment.GetEnvironmentVariable "ASPNETCORE_ENVIRONMENT" = Environments.Development
-
-/// Pipeline wrappers to add different kinds of dependencies into the DI container.
-module DI =
- open Microsoft.Extensions.DependencyInjection
- /// Shortcut for services.AddSingleton<'T>
- let singleton<'T when 'T: not struct> (services: IServiceCollection) = services.AddSingleton<'T>()
- /// Shortcut for services.AddTransient<'T>
- let transient<'T when 'T: not struct> (services: IServiceCollection) = services.AddTransient<'T>()
- /// Shortcut for services.AddScoped<'T>
- let scoped<'T when 'T: not struct> (services: IServiceCollection) = services.AddScoped<'T>()
-
-[]
-module Extensions =
- open Microsoft.AspNetCore.Http
- open Microsoft.Extensions.Configuration
-
- type HttpContext with
-
- /// Gets the current IConfiguration service from the IOC container.
- member this.Configuration = this.GetService()
-
-[]
-type Api =
- /// Quickly creates a Fable Remoting API as an HTTP Handler for Giraffe. For reference documentation, see https://zaid-ajaj.github.io/Fable.Remoting/#/server-setup/saturn.
- /// The Fable Remoting API. It must take in an `HTTPContext` as the only argument.
- /// A function which takes the name of the API and the name of the method and returns the route to the server. Defaults to `/api/{api name}/{method name}` e.g. `/api/ITodoApi/GetTodos`.
- /// A function which takes an `Exception` and a `RouteInfo` and returns a Giraffe Handler. Defaults to `Ignore` the exception i.e. it will not be cascaded.
- /// A function which takes the current Fable Remoting options and returns a new set of options. Useful for applying any custom options.
- static member make(api, ?routeBuilder, ?errorHandler, ?customOptions) =
- let routeBuilder = defaultArg routeBuilder (sprintf "/api/%s/%s")
- let errorHandler = defaultArg errorHandler (fun _ _ -> Ignore)
- let customOptions = defaultArg customOptions id
-
- Remoting.createApi ()
- |> Remoting.withRouteBuilder routeBuilder
- |> Remoting.withErrorHandler errorHandler
- |> customOptions
- |> Remoting.fromContext api
- |> Remoting.buildHttpHandler
\ No newline at end of file
diff --git a/src/SAFE.Server/paket.references b/src/SAFE.Server/paket.references
index a402c76..db9ab92 100644
--- a/src/SAFE.Server/paket.references
+++ b/src/SAFE.Server/paket.references
@@ -1,3 +1,4 @@
FSharp.Core
Saturn
-Fable.Remoting.Giraffe
\ No newline at end of file
+Fable.Remoting.Giraffe
+SAFE.Server.Utils
\ No newline at end of file
diff --git a/src/SAFE.Server/readme.md b/src/SAFE.Server/readme.md
index 6d358b9..396f70e 100644
--- a/src/SAFE.Server/readme.md
+++ b/src/SAFE.Server/readme.md
@@ -1 +1 @@
-Contains dependencies required for the SAFE Stack server component, as well as a set of helper types and utilities.
\ No newline at end of file
+Contains dependencies required for the SAFE Stack server component
\ No newline at end of file
diff --git a/test/SAFE.Client.Tests/package.json b/test/SAFE.Client.Tests/package.json
index 4719eb1..9044238 100644
--- a/test/SAFE.Client.Tests/package.json
+++ b/test/SAFE.Client.Tests/package.json
@@ -1,9 +1,9 @@
{
-"type" : "module",
- "scripts": {
- "pretest": "dotnet fable -o bld",
- "test": "mocha bld"
- },
+ "type": "module",
+ "scripts": {
+ "pretest": "dotnet fable -o bld --noCache",
+ "test": "mocha bld"
+ },
"devDependencies": {
"mocha": "^10.8.2"
}