on master help sign in
Introduce Identity and Session types to reduce duplication and improve type safety in client
de34c51iko
client/App.fs+86−71
@@ -29,7 +29,7 @@ module App =
29 type Page =
29 type Page =
30 | Setup
30 | Setup
31 | Conversations
31 | Conversations
32− | Chat of string
32+ | Chat of pubkey: string
33 | AddContact
33 | AddContact
34 | Settings
34 | Settings
35
35
@@ -38,12 +38,15 @@ module App =
38 | Connecting
38 | Connecting
39 | Online of token: string
39 | Online of token: string
40
40
41+ type Identity = { PrivKey: byte[]; PubKeyHex: string }
42+
43+ type Session = { Url: string; Token: string; Identity: Identity }
44+
41 // ── Model ──
45 // ── Model ──
42
46
43 type Model =
47 type Model =
44 { Page: Page
48 { Page: Page
45− PrivKey: byte[] option
49+ Identity: Identity option
46− PubKeyHex: string
47 ServerUrl: string
50 ServerUrl: string
48 Conn: ConnState
51 Conn: ConnState
49 Contacts: Contact list
52 Contacts: Contact list
@@ -61,7 +64,7 @@ module App =
61 type Msg =
64 type Msg =
62 | Nav of Page
65 | Nav of Page
63 | GenIdentity
66 | GenIdentity
64− | IdentityReady of byte[] * byte[]
67+ | IdentityReady of Identity
65 | SetServerUrl of string
68 | SetServerUrl of string
66 | DoConnect
69 | DoConnect
67 | AuthOk of string
70 | AuthOk of string
@@ -69,12 +72,12 @@ module App =
69 | DoDisconnect
72 | DoDisconnect
70 | SetCompose of string
73 | SetCompose of string
71 | DoSend
74 | DoSend
72− | Sent of string * string
75+ | Sent of recipient: string * status: string
73 | SetNewPubkey of string
76 | SetNewPubkey of string
74 | SetNewNickname of string
77 | SetNewNickname of string
75 | DoSaveContact
78 | DoSaveContact
76 | DoDeleteContact of string
79 | DoDeleteContact of string
77− | PollResult of IncomingMessage list * string * uint64
80+ | PollResult of messages: IncomingMessage list * status: string * cursor: uint64
78 | DoPoll
81 | DoPoll
79 | CopyPubKey
82 | CopyPubKey
80 | DismissError
83 | DismissError
@@ -83,9 +86,9 @@ module App =
83
86
84 type CmdMsg =
87 type CmdMsg =
85 | CmdGenIdentity
88 | CmdGenIdentity
86− | CmdConnect of url: string * pubHex: string * privKey: byte[]
89+ | CmdConnect of url: string * identity: Identity
87− | CmdSend of url: string * token: string * recipientHex: string * privKey: byte[] * text: string
90+ | CmdSend of session: Session * recipientHex: string * text: string
88− | CmdPoll of url: string * token: string * privKey: byte[] * cursor: uint64
91+ | CmdPoll of session: Session * cursor: uint64
89 | CmdCopyToClipboard of string
92 | CmdCopyToClipboard of string
90
93
91 // ── Helpers ──
94 // ── Helpers ──
@@ -112,12 +115,19 @@ module App =
112 else
115 else
113 contacts @ [ contact ]
116 contacts @ [ contact ]
114
117
118+ let private pubKeyHex model =
119+ model.Identity |> Option.map _.PubKeyHex |> Option.defaultValue ""
120+
121+ let private trySession model =
122+ match model.Identity, model.Conn with
123+ | Some id, Online token -> Some { Url = model.ServerUrl; Token = token; Identity = id }
124+ | _ -> None
125+
115 // ── Init ──
126 // ── Init ──
116
127
117 let init () =
128 let init () =
118 { Page = Setup
129 { Page = Setup
119− PrivKey = None
130+ Identity = None
120− PubKeyHex = ""
121 ServerUrl = "http://localhost:8080"
131 ServerUrl = "http://localhost:8080"
122 Conn = Offline
132 Conn = Offline
123 PollStatus = ""
133 PollStatus = ""
@@ -139,26 +149,23 @@ module App =
139
149
140 | GenIdentity -> model, [ CmdGenIdentity ]
150 | GenIdentity -> model, [ CmdGenIdentity ]
141
151
142− | IdentityReady(priv, pub) ->
152+ | IdentityReady identity ->
143− { model with
144− PrivKey = Some priv
145− PubKeyHex = Crypto.toHex pub
146− Page = Settings },
153+ { model with Identity = Some identity; Page = Settings }, []
147− []
148
154
149 | SetServerUrl url -> { model with ServerUrl = url }, []
155 | SetServerUrl url -> { model with ServerUrl = url }, []
150
156
151 | DoConnect ->
157 | DoConnect ->
152− match model.PrivKey with
158+ match model.Identity with
153− | Some priv ->
159+ | Some id ->
154− { model with Conn = Connecting }, [ CmdConnect(model.ServerUrl, model.PubKeyHex, priv) ]
160+ { model with Conn = Connecting }, [ CmdConnect(model.ServerUrl, id) ]
155 | None -> { model with Error = Some "No identity generated" }, []
161 | None -> { model with Error = Some "No identity generated" }, []
156
162
157 | AuthOk token ->
163 | AuthOk token ->
158− match model.PrivKey with
164+ match model.Identity with
159− | Some priv ->
165+ | Some id ->
166+ let session = { Url = model.ServerUrl; Token = token; Identity = id }
160 { model with Conn = Online token; Page = Conversations; Error = None },
167 { model with Conn = Online token; Page = Conversations; Error = None },
161− [ CmdPoll(model.ServerUrl, token, priv, model.PollCursor) ]
168+ [ CmdPoll(session, model.PollCursor) ]
162 | None ->
169 | None ->
163 { model with Error = Some "No identity generated" }, []
170 { model with Error = Some "No identity generated" }, []
164
171
@@ -169,8 +176,8 @@ module App =
169 | SetCompose text -> { model with ComposeText = text }, []
176 | SetCompose text -> { model with ComposeText = text }, []
170
177
171 | DoSend ->
178 | DoSend ->
172− match model.Page, model.Conn, model.PrivKey with
179+ match model.Page, trySession model with
173− | Chat pk, Online token, Some priv when model.ComposeText <> "" ->
180+ | Chat pk, Some session when model.ComposeText <> "" ->
174 let text = model.ComposeText
181 let text = model.ComposeText
175 let outMsg =
182 let outMsg =
176 { Id = Guid.NewGuid().ToString()
183 { Id = Guid.NewGuid().ToString()
@@ -182,7 +189,7 @@ module App =
182 { model with
189 { model with
183 ComposeText = ""
190 ComposeText = ""
184 Messages = model.Messages |> Map.add pk (msgs @ [ outMsg ]) },
191 Messages = model.Messages |> Map.add pk (msgs @ [ outMsg ]) },
185− [ CmdSend(model.ServerUrl, token, pk, priv, text) ]
192+ [ CmdSend(session, pk, text) ]
186 | _ -> model, []
193 | _ -> model, []
187
194
188 | Sent(_, status) -> { model with LastSendStatus = status }, []
195 | Sent(_, status) -> { model with LastSendStatus = status }, []
@@ -208,14 +215,14 @@ module App =
208 Messages = m.Messages |> Map.add msg.FromPubkey (msgs @ [ chatMsg ]) })
215 Messages = m.Messages |> Map.add msg.FromPubkey (msgs @ [ chatMsg ]) })
209 { model with PollStatus = status; PollCursor = newCursor }
216 { model with PollStatus = status; PollCursor = newCursor }
210
217
211− match model'.Conn, model'.PrivKey with
218+ match trySession model' with
212− | Online token, Some priv -> model', [ CmdPoll(model'.ServerUrl, token, priv, model'.PollCursor) ]
219+ | Some session -> model', [ CmdPoll(session, model'.PollCursor) ]
213− | _ -> model', []
220+ | None -> model', []
214
221
215 | DoPoll ->
222 | DoPoll ->
216− match model.Conn, model.PrivKey with
223+ match trySession model with
217− | Online token, Some priv -> model, [ CmdPoll(model.ServerUrl, token, priv, model.PollCursor) ]
224+ | Some session -> model, [ CmdPoll(session, model.PollCursor) ]
218− | _ -> model, []
225+ | None -> model, []
219
226
220 | SetNewPubkey pk -> { model with NewPubkey = pk }, []
227 | SetNewPubkey pk -> { model with NewPubkey = pk }, []
221 | SetNewNickname nn -> { model with NewNickname = nn }, []
228 | SetNewNickname nn -> { model with NewNickname = nn }, []
@@ -240,7 +247,10 @@ module App =
240 Contacts = model.Contacts |> List.filter (fun c -> c.Pubkey <> pk) },
247 Contacts = model.Contacts |> List.filter (fun c -> c.Pubkey <> pk) },
241 []
248 []
242
249
250+ | CopyPubKey ->
251+ match model.Identity with
243− | CopyPubKey -> model, [ CmdCopyToClipboard model.PubKeyHex ]
252+ | Some id -> model, [ CmdCopyToClipboard id.PubKeyHex ]
253+ | None -> model, []
244
254
245 | DismissError -> { model with Error = None }, []
255 | DismissError -> { model with Error = None }, []
246
256
@@ -255,20 +265,44 @@ module App =
255 }))
265 }))
256 |> ignore)
266 |> ignore)
257
267
268+ let private decryptEvent (privKey: byte[]) (payload: JsonElement) =
269+ try
270+ let blobHex = payload.GetProperty("encryptedBlob").GetString()
271+ let tsEl = payload.GetProperty("timestamp")
272+ let ts =
273+ if tsEl.ValueKind = JsonValueKind.String then Int64.Parse(tsEl.GetString())
274+ else tsEl.GetInt64()
275+ let blob = Crypto.fromHex blobHex
276+
277+ match Crypto.decrypt privKey blob with
278+ | Some(plaintext, senderHex) ->
279+ let ptDoc = JsonDocument.Parse(plaintext)
280+ let ptRoot = ptDoc.RootElement
281+
282+ if ptRoot.GetProperty("type").GetString() = "text" then
283+ let msgId = ptRoot.GetProperty("id").GetString()
284+ let body = ptRoot.GetProperty("body").GetString()
285+ Ok { FromPubkey = senderHex; Body = body; Timestamp = ts; MessageId = msgId }
286+ else
287+ Error "unknown type"
288+ | None -> Error "decrypt failed"
289+ with ex ->
290+ Error $"parse: {ex.Message}"
291+
258 let mapCmd cmdMsg =
292 let mapCmd cmdMsg =
259 match cmdMsg with
293 match cmdMsg with
260 | CmdGenIdentity ->
294 | CmdGenIdentity ->
261 asyncCmd (async {
295 asyncCmd (async {
262 let priv, pub = Crypto.generateIdentity ()
296 let priv, pub = Crypto.generateIdentity ()
263− return IdentityReady(priv, pub)
297+ return IdentityReady { PrivKey = priv; PubKeyHex = Crypto.toHex pub }
264 })
298 })
265
299
266− | CmdConnect(url, pubHex, privKey) ->
300+ | CmdConnect(url, identity) ->
267 asyncCmd (async {
301 asyncCmd (async {
268 try
302 try
269− let! challenge = ApiClient.requestChallenge url pubHex
303+ let! challenge = ApiClient.requestChallenge url identity.PubKeyHex
270− let sigHex = Crypto.signChallenge privKey challenge
304+ let sigHex = Crypto.signChallenge identity.PrivKey challenge
271− let! (token, _) = ApiClient.verify url pubHex challenge sigHex
305+ let! (token, _) = ApiClient.verify url identity.PubKeyHex challenge sigHex
272
306
273 if String.IsNullOrEmpty(token) then
307 if String.IsNullOrEmpty(token) then
274 return AuthErr "Authentication rejected by server"
308 return AuthErr "Authentication rejected by server"
@@ -278,10 +312,10 @@ module App =
278 return AuthErr ex.Message
312 return AuthErr ex.Message
279 })
313 })
280
314
281− | CmdSend(url, token, recipientHex, privKey, text) ->
315+ | CmdSend(session, recipientHex, text) ->
282 asyncCmd (async {
316 asyncCmd (async {
283 try
317 try
284− let pubKey = privKey.[32..63]
318+ let pubKey = session.Identity.PrivKey.[32..63]
285 let recipPub = Crypto.fromHex recipientHex
319 let recipPub = Crypto.fromHex recipientHex
286 let id = Guid.NewGuid().ToString()
320 let id = Guid.NewGuid().ToString()
287 let ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
321 let ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
@@ -289,52 +323,33 @@ module App =
289 let payload =
323 let payload =
290 $"""{{ "type": "text", "id": "{id}", "timestamp": {ts}, "body": {JsonSerializer.Serialize(text)} }}"""
324 $"""{{ "type": "text", "id": "{id}", "timestamp": {ts}, "body": {JsonSerializer.Serialize(text)} }}"""
291
325
292− let blob = Crypto.encrypt privKey pubKey recipPub payload
326+ let blob = Crypto.encrypt session.Identity.PrivKey pubKey recipPub payload
293 let blobHex = Crypto.toHex blob
327 let blobHex = Crypto.toHex blob
294− let! (status, msgId) = ApiClient.sendMessage url token recipientHex blobHex ts
328+ let! (status, msgId) = ApiClient.sendMessage session.Url session.Token recipientHex blobHex ts
295 return Sent(recipientHex, $"{status}:{msgId}")
329 return Sent(recipientHex, $"{status}:{msgId}")
296 with ex ->
330 with ex ->
297 return DismissError // TODO: show send errors properly
331 return DismissError // TODO: show send errors properly
298 })
332 })
299
333
300− | CmdPoll(url, token, privKey, cursor) ->
334+ | CmdPoll(session, cursor) ->
301 asyncCmd (async {
335 asyncCmd (async {
302 try
336 try
303− let! (events, newCursor) = ApiClient.poll url token cursor
337+ let! (events, newCursor) = ApiClient.poll session.Url session.Token cursor
304 let messages = ResizeArray()
338 let messages = ResizeArray()
305 let ackIds = ResizeArray()
339 let ackIds = ResizeArray()
306 let errors = ResizeArray()
340 let errors = ResizeArray()
307
341
308 for (evtId, evtType, payload) in events do
342 for (evtId, evtType, payload) in events do
309 if evtType = "message" then
343 if evtType = "message" then
310− try
344+ match decryptEvent session.Identity.PrivKey payload with
311− let blobHex = payload.GetProperty("encryptedBlob").GetString()
345+ | Ok msg ->
312− let tsEl = payload.GetProperty("timestamp")
346+ messages.Add(msg)
313− let ts =
314− if tsEl.ValueKind = JsonValueKind.String then
315− Int64.Parse(tsEl.GetString())
316− else
317− tsEl.GetInt64()
318− let blob = Crypto.fromHex blobHex
319−
320− match Crypto.decrypt privKey blob with
321− | Some(plaintext, senderHex) ->
322− let ptDoc = JsonDocument.Parse(plaintext)
323− let ptRoot = ptDoc.RootElement
324−
325− if ptRoot.GetProperty("type").GetString() = "text" then
326− let msgId = ptRoot.GetProperty("id").GetString()
327− let body = ptRoot.GetProperty("body").GetString()
328− messages.Add({ FromPubkey = senderHex; Body = body; Timestamp = ts; MessageId = msgId })
329−
330− ackIds.Add(evtId)
347+ ackIds.Add(evtId)
331− | None ->
348+ | Error err ->
332− errors.Add("decrypt failed")
333− with ex ->
334− errors.Add($"parse: {ex.Message}")
349+ errors.Add(err)
335
350
336 if ackIds.Count > 0 then
351 if ackIds.Count > 0 then
337− do! ApiClient.ackMessages url token (Seq.toList ackIds)
352+ do! ApiClient.ackMessages session.Url session.Token (Seq.toList ackIds)
338
353
339 let now = DateTimeOffset.UtcNow.ToString("HH:mm:ss")
354 let now = DateTimeOffset.UtcNow.ToString("HH:mm:ss")
340
355
@@ -387,7 +402,7 @@ module App =
387
402
388 Label("Your Public Key:")
403 Label("Your Public Key:")
389
404
390− Label(model.PubKeyHex)
405+ Label(pubKeyHex model)
391 .font(size = 9.)
406 .font(size = 9.)
392
407
393 Button("Copy Public Key", CopyPubKey)
408 Button("Copy Public Key", CopyPubKey)
client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.app/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.app/Skrepka.pdb

Binary file not shown.

client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.app/_CodeSignature/CodeResources+4−4
@@ -162,11 +162,11 @@
162 </data>
162 </data>
163 <key>Skrepka.dll</key>
163 <key>Skrepka.dll</key>
164 <data>
164 <data>
165− r/iZ42W7ZFINtqwUABz6aXZb5Pk=
165+ CeTBQvhkvL1/EcEY1Kac+n8Bx9Q=
166 </data>
166 </data>
167 <key>Skrepka.pdb</key>
167 <key>Skrepka.pdb</key>
168 <data>
168 <data>
169− zasvE0a+Wo6/nzeFiWsUuXJ8xvk=
169+ 6zfAl+vMNts+V2cDihTfBAU7qE8=
170 </data>
170 </data>
171 <key>Sodium.Core.dll</key>
171 <key>Sodium.Core.dll</key>
172 <data>
172 <data>
@@ -1382,14 +1382,14 @@
1382 <dict>
1382 <dict>
1383 <key>hash2</key>
1383 <key>hash2</key>
1384 <data>
1384 <data>
1385− 0RquViMJ5bGDhRzrCP7NG5r1xRpnBdss3r87Qcq7Kms=
1385+ hOO3wSiltqVfbPfmOuTAxv7lmEs2WLwwm5MvkCv2Zes=
1386 </data>
1386 </data>
1387 </dict>
1387 </dict>
1388 <key>Skrepka.pdb</key>
1388 <key>Skrepka.pdb</key>
1389 <dict>
1389 <dict>
1390 <key>hash2</key>
1390 <key>hash2</key>
1391 <data>
1391 <data>
1392− cUBl9b6I54xAic0oMZhTa+FIkXNVu6LHRMxd5Q4QIVE=
1392+ kmjBQN07kWsY1/HXhYkdO3kjdifQq2fFkig7HxmMRYk=
1393 </data>
1393 </data>
1394 </dict>
1394 </dict>
1395 <key>Sodium.Core.dll</key>
1395 <key>Sodium.Core.dll</key>
client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-ios/iossimulator-arm64/Skrepka.pdb

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.app/Contents/MonoBundle/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.app/Contents/MonoBundle/Skrepka.pdb

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.app/Contents/_CodeSignature/CodeResources+2−2
@@ -240,14 +240,14 @@
240 <dict>
240 <dict>
241 <key>hash2</key>
241 <key>hash2</key>
242 <data>
242 <data>
243− 1YMNcB7xDPXVmXTy/5unpWgH6sFYXx+ajmUweDpLEA0=
243+ Vn+FDLReC1N3ufTvaWxAof2GDpXaFK15kE1qnv30lfo=
244 </data>
244 </data>
245 </dict>
245 </dict>
246 <key>MonoBundle/Skrepka.pdb</key>
246 <key>MonoBundle/Skrepka.pdb</key>
247 <dict>
247 <dict>
248 <key>hash2</key>
248 <key>hash2</key>
249 <data>
249 <data>
250− W+NR34Zhwrd+2gZJtPCFpFd/OzgeckZ7aIj40k0IFrw=
250+ a7AKD+ddEPH8nfW+/IfEzsna2CWaA+HD2IezDhWtizg=
251 </data>
251 </data>
252 </dict>
252 </dict>
253 <key>MonoBundle/Sodium.Core.dll</key>
253 <key>MonoBundle/Sodium.Core.dll</key>
client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.dll

Binary file not shown.

client/bin/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.AssemblyInfo.fs+1−1
@@ -10,7 +10,7 @@ open System.Reflection
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
13−[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+ac8c87f242c436f4a1bad89ff035eecf82a2bb15")>]
13+[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+3964fd862ab856cbb1406ad52f4523dd2c119a53")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.AssemblyInfoInputs.cache+1−1
@@ -1 +1 @@
1−dcd7cb11d85d1b7e19d268b4a83cd5e3289a020ad2c4daa5bfa952d60173855b
1+d9154c820b51f064aa00760cde29d657ca60e63964b18a475cf0ad904f6074d9
client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.fsproj.FileListAbsolute.txt+22
@@ -333,3 +333,25 @@
333/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.pdb
333/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.pdb
334/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.genruntimeconfig.cache
334/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.genruntimeconfig.cache
335/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/ref/Skrepka.dll
335/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/ref/Skrepka.dll
336+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Fabulous.stamp
337+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Fabulous.MauiControls.stamp
338+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/FSharp.Core.stamp
339+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Configuration.Abstractions.stamp
340+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Configuration.stamp
341+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.DependencyInjection.Abstractions.stamp
342+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.DependencyInjection.stamp
343+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Diagnostics.Abstractions.stamp
344+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.FileProviders.Abstractions.stamp
345+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Hosting.Abstractions.stamp
346+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Logging.Abstractions.stamp
347+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Logging.stamp
348+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Options.stamp
349+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Extensions.Primitives.stamp
350+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Controls.Compatibility.stamp
351+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Controls.stamp
352+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Controls.Xaml.stamp
353+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.stamp
354+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Essentials.stamp
355+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Microsoft.Maui.Graphics.stamp
356+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/resourcestamps/Sodium.Core.stamp
357+/Users/iko/Developer/skrepka/client/obj/Debug/net10.0-ios/iossimulator-arm64/unpack/Microsoft.Maui.Essentials/BundleResource/xml/microsoft_maui_essentials_fileprovider_file_paths.xml
client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/Skrepka.sourcelink.json+1−1
@@ -1 +1 @@
1−{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/ac8c87f242c436f4a1bad89ff035eecf82a2bb15/*"}}
1+{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/3964fd862ab856cbb1406ad52f4523dd2c119a53/*"}}
client/obj/Debug/net10.0-ios/iossimulator-arm64/linked/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/linked/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/ref/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-ios/iossimulator-arm64/refint/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.AssemblyInfo.fs+1−1
@@ -10,7 +10,7 @@ open System.Reflection
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
10[<assembly: System.Reflection.AssemblyCompanyAttribute("Skrepka")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
11[<assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
12[<assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")>]
13−[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+82fcdc7935484a61c2a22433c36ce8637ad179c8")>]
13+[<assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0+3964fd862ab856cbb1406ad52f4523dd2c119a53")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
14[<assembly: System.Reflection.AssemblyProductAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
15[<assembly: System.Reflection.AssemblyTitleAttribute("Skrepka")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
16[<assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")>]
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.AssemblyInfoInputs.cache+1−1
@@ -1 +1 @@
1−0a759b94d305484f3281f9f60b3791a1832d7dde46f8f7a622e4deaf1e156292
1+7730279932bc940b8619bf395e54c158db9633d590a5927d35446a96c7518d10
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/Skrepka.sourcelink.json+1−1
@@ -1 +1 @@
1−{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/82fcdc7935484a61c2a22433c36ce8637ad179c8/*"}}
1+{"documents":{"/Users/iko/Developer/skrepka/*":"https://raw.githubusercontent.com/ilyakooo0/skrepka/3964fd862ab856cbb1406ad52f4523dd2c119a53/*"}}
client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/linked/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/linked/Skrepka.pdb

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/ref/Skrepka.dll

Binary file not shown.

client/obj/Debug/net10.0-maccatalyst/maccatalyst-arm64/refint/Skrepka.dll

Binary file not shown.