create-sia-app 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/package.json +2 -6
  2. package/template/AGENTS.md +143 -0
  3. package/template/CLAUDE.md +25 -46
  4. package/template/README.md +6 -12
  5. package/template/_gitignore +0 -1
  6. package/template/dist/assets/index-BEylY2j7.css +1 -0
  7. package/template/dist/assets/index-CnYqArKN.js +8741 -0
  8. package/template/dist/assets/sia_bg-BTOHUC1A.wasm +0 -0
  9. package/template/dist/assets/slab-download-worker-DhW6ZBJs.js +2 -0
  10. package/template/dist/assets/slab-upload-worker-B2uSB2iY.js +2 -0
  11. package/template/dist/index.html +13 -0
  12. package/template/e2e/smoke.spec.ts +20 -0
  13. package/template/index.html +0 -1
  14. package/template/package.json +2 -2
  15. package/template/playwright.config.ts +13 -0
  16. package/template/src/components/Navbar.tsx +3 -3
  17. package/template/src/components/auth/ApproveScreen.tsx +10 -13
  18. package/template/src/components/auth/AuthFlow.tsx +13 -10
  19. package/template/src/components/auth/ConnectScreen.tsx +2 -3
  20. package/template/src/components/auth/RecoveryScreen.tsx +7 -7
  21. package/template/src/components/upload/UploadZone.tsx +192 -74
  22. package/template/src/index.css +14 -4
  23. package/template/src/stores/auth.ts +6 -12
  24. package/template/test-results/.last-run.json +4 -0
  25. package/template/tsconfig.app.json +1 -1
  26. package/template/tsconfig.node.json +0 -1
  27. package/template/vite.config.ts +2 -3
  28. package/template/rust/README.md +0 -16
  29. package/template/rust/sia-sdk-rs/.changeset/added_cancel_function_to_cancel_inflight_packed_uploads.md +0 -6
  30. package/template/rust/sia-sdk-rs/.changeset/check_if_we_have_enough_hosts_prior_to_encoding_in_upload_slabs.md +0 -16
  31. package/template/rust/sia-sdk-rs/.changeset/fix_slab_length_in_packed_object.md +0 -5
  32. package/template/rust/sia-sdk-rs/.changeset/fix_upload_racing_race_conditon.md +0 -13
  33. package/template/rust/sia-sdk-rs/.changeset/improved_parallelism_of_packed_uploads.md +0 -5
  34. package/template/rust/sia-sdk-rs/.changeset/progress_callback_will_now_be_called_as_expected_for_packed_uploads.md +0 -5
  35. package/template/rust/sia-sdk-rs/.github/dependabot.yml +0 -10
  36. package/template/rust/sia-sdk-rs/.github/workflows/main.yml +0 -36
  37. package/template/rust/sia-sdk-rs/.github/workflows/prepare-release.yml +0 -34
  38. package/template/rust/sia-sdk-rs/.github/workflows/release.yml +0 -30
  39. package/template/rust/sia-sdk-rs/.rustfmt.toml +0 -4
  40. package/template/rust/sia-sdk-rs/Cargo.lock +0 -4127
  41. package/template/rust/sia-sdk-rs/Cargo.toml +0 -3
  42. package/template/rust/sia-sdk-rs/LICENSE +0 -21
  43. package/template/rust/sia-sdk-rs/README.md +0 -30
  44. package/template/rust/sia-sdk-rs/indexd/CHANGELOG.md +0 -79
  45. package/template/rust/sia-sdk-rs/indexd/Cargo.toml +0 -79
  46. package/template/rust/sia-sdk-rs/indexd/benches/upload.rs +0 -258
  47. package/template/rust/sia-sdk-rs/indexd/src/app_client.rs +0 -1710
  48. package/template/rust/sia-sdk-rs/indexd/src/builder.rs +0 -354
  49. package/template/rust/sia-sdk-rs/indexd/src/download.rs +0 -379
  50. package/template/rust/sia-sdk-rs/indexd/src/hosts.rs +0 -659
  51. package/template/rust/sia-sdk-rs/indexd/src/lib.rs +0 -827
  52. package/template/rust/sia-sdk-rs/indexd/src/mock.rs +0 -162
  53. package/template/rust/sia-sdk-rs/indexd/src/object_encryption.rs +0 -125
  54. package/template/rust/sia-sdk-rs/indexd/src/quic.rs +0 -575
  55. package/template/rust/sia-sdk-rs/indexd/src/rhp4.rs +0 -52
  56. package/template/rust/sia-sdk-rs/indexd/src/slabs.rs +0 -497
  57. package/template/rust/sia-sdk-rs/indexd/src/upload.rs +0 -629
  58. package/template/rust/sia-sdk-rs/indexd/src/wasm_time.rs +0 -41
  59. package/template/rust/sia-sdk-rs/indexd/src/web_transport.rs +0 -398
  60. package/template/rust/sia-sdk-rs/indexd_ffi/CHANGELOG.md +0 -76
  61. package/template/rust/sia-sdk-rs/indexd_ffi/Cargo.toml +0 -47
  62. package/template/rust/sia-sdk-rs/indexd_ffi/examples/python/README.md +0 -10
  63. package/template/rust/sia-sdk-rs/indexd_ffi/examples/python/example.py +0 -130
  64. package/template/rust/sia-sdk-rs/indexd_ffi/src/bin/uniffi-bindgen.rs +0 -3
  65. package/template/rust/sia-sdk-rs/indexd_ffi/src/builder.rs +0 -377
  66. package/template/rust/sia-sdk-rs/indexd_ffi/src/io.rs +0 -155
  67. package/template/rust/sia-sdk-rs/indexd_ffi/src/lib.rs +0 -1039
  68. package/template/rust/sia-sdk-rs/indexd_ffi/src/logging.rs +0 -58
  69. package/template/rust/sia-sdk-rs/indexd_ffi/src/tls.rs +0 -23
  70. package/template/rust/sia-sdk-rs/indexd_wasm/Cargo.toml +0 -33
  71. package/template/rust/sia-sdk-rs/indexd_wasm/src/lib.rs +0 -818
  72. package/template/rust/sia-sdk-rs/knope.toml +0 -54
  73. package/template/rust/sia-sdk-rs/sia_derive/CHANGELOG.md +0 -38
  74. package/template/rust/sia-sdk-rs/sia_derive/Cargo.toml +0 -19
  75. package/template/rust/sia-sdk-rs/sia_derive/src/lib.rs +0 -278
  76. package/template/rust/sia-sdk-rs/sia_sdk/CHANGELOG.md +0 -91
  77. package/template/rust/sia-sdk-rs/sia_sdk/Cargo.toml +0 -59
  78. package/template/rust/sia-sdk-rs/sia_sdk/benches/merkle_root.rs +0 -12
  79. package/template/rust/sia-sdk-rs/sia_sdk/src/blake2.rs +0 -22
  80. package/template/rust/sia-sdk-rs/sia_sdk/src/consensus.rs +0 -767
  81. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding/v1.rs +0 -257
  82. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding/v2.rs +0 -291
  83. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding.rs +0 -26
  84. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding_async/v2.rs +0 -367
  85. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding_async.rs +0 -6
  86. package/template/rust/sia-sdk-rs/sia_sdk/src/encryption.rs +0 -303
  87. package/template/rust/sia-sdk-rs/sia_sdk/src/erasure_coding.rs +0 -347
  88. package/template/rust/sia-sdk-rs/sia_sdk/src/lib.rs +0 -15
  89. package/template/rust/sia-sdk-rs/sia_sdk/src/macros.rs +0 -435
  90. package/template/rust/sia-sdk-rs/sia_sdk/src/merkle.rs +0 -112
  91. package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/merkle.rs +0 -357
  92. package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/rpc.rs +0 -1507
  93. package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/types.rs +0 -146
  94. package/template/rust/sia-sdk-rs/sia_sdk/src/rhp.rs +0 -7
  95. package/template/rust/sia-sdk-rs/sia_sdk/src/seed.rs +0 -278
  96. package/template/rust/sia-sdk-rs/sia_sdk/src/signing.rs +0 -236
  97. package/template/rust/sia-sdk-rs/sia_sdk/src/types/common.rs +0 -677
  98. package/template/rust/sia-sdk-rs/sia_sdk/src/types/currency.rs +0 -450
  99. package/template/rust/sia-sdk-rs/sia_sdk/src/types/specifier.rs +0 -110
  100. package/template/rust/sia-sdk-rs/sia_sdk/src/types/spendpolicy.rs +0 -778
  101. package/template/rust/sia-sdk-rs/sia_sdk/src/types/utils.rs +0 -117
  102. package/template/rust/sia-sdk-rs/sia_sdk/src/types/v1.rs +0 -1737
  103. package/template/rust/sia-sdk-rs/sia_sdk/src/types/v2.rs +0 -1726
  104. package/template/rust/sia-sdk-rs/sia_sdk/src/types/work.rs +0 -59
  105. package/template/rust/sia-sdk-rs/sia_sdk/src/types.rs +0 -16
  106. package/template/scripts/setup-rust.js +0 -29
  107. package/template/src/lib/format.ts +0 -35
  108. package/template/src/lib/hex.ts +0 -13
  109. package/template/src/lib/sdk.ts +0 -25
  110. package/template/src/lib/wasm-env.ts +0 -5
  111. package/template/wasm/indexd_wasm/indexd_wasm.d.ts +0 -309
  112. package/template/wasm/indexd_wasm/indexd_wasm.js +0 -1507
  113. package/template/wasm/indexd_wasm/indexd_wasm_bg.wasm +0 -0
  114. package/template/wasm/indexd_wasm/package.json +0 -31
@@ -1,379 +0,0 @@
1
- use std::collections::VecDeque;
2
- use std::fmt::Debug;
3
- use std::sync::Arc;
4
- use std::time::Duration;
5
-
6
- use log::debug;
7
- use sia::encryption::{EncryptionKey, encrypt_shard};
8
- use sia::erasure_coding::{self, ErasureCoder};
9
- use sia::rhp::SEGMENT_SIZE;
10
- use sia::signing::PrivateKey;
11
- use thiserror::Error;
12
- use tokio::io::AsyncWriteExt;
13
- use tokio::sync::{OwnedSemaphorePermit, Semaphore, mpsc};
14
- use tokio::task::JoinSet;
15
- #[cfg(not(target_arch = "wasm32"))]
16
- use tokio::task::spawn_blocking;
17
- #[cfg(not(target_arch = "wasm32"))]
18
- use tokio::time::error::Elapsed;
19
- #[cfg(not(target_arch = "wasm32"))]
20
- use tokio::time::sleep;
21
- #[cfg(target_arch = "wasm32")]
22
- use crate::wasm_time::sleep;
23
-
24
- use crate::rhp4::RHP4Client;
25
- use crate::{Hosts, Object, Sector};
26
-
27
- /// Spawns a task on a [`JoinSet`]. Uses `spawn` on native (requires `Send`)
28
- /// and `spawn_local` on WASM (runs on the current [`tokio::task::LocalSet`]).
29
- macro_rules! join_set_spawn {
30
- ($set:expr, $fut:expr) => {{
31
- #[cfg(not(target_arch = "wasm32"))]
32
- $set.spawn($fut);
33
- #[cfg(target_arch = "wasm32")]
34
- $set.spawn_local($fut);
35
- }};
36
- }
37
-
38
- #[derive(Debug, Error)]
39
- pub enum DownloadError {
40
- #[error("I/O error: {0}")]
41
- Io(#[from] std::io::Error),
42
-
43
- #[error("encoder error: {0}")]
44
- Encoder(#[from] erasure_coding::Error),
45
-
46
- #[error("not enough shards: {0}/{1}")]
47
- NotEnoughShards(u8, u8),
48
-
49
- #[error("invalid range: {0}-{1}")]
50
- OutOfRange(usize, usize),
51
-
52
- #[cfg(not(target_arch = "wasm32"))]
53
- #[error("timeout error: {0}")]
54
- Timeout(#[from] Elapsed),
55
-
56
- #[error("semaphore error: {0}")]
57
- SemaphoreError(#[from] tokio::sync::AcquireError),
58
-
59
- #[error("join error: {0}")]
60
- JoinError(#[from] tokio::task::JoinError),
61
-
62
- #[error("invalid slab: {0}")]
63
- InvalidSlab(String),
64
-
65
- #[error("rhp4 error: {0}")]
66
- Rhp4(#[from] crate::rhp4::Error),
67
-
68
- #[error("custom error: {0}")]
69
- Custom(String),
70
- }
71
-
72
- pub struct DownloadOptions {
73
- /// Maximum number of concurrent sector downloads.
74
- pub max_inflight: usize,
75
- pub offset: u64,
76
- pub length: Option<u64>,
77
-
78
- /// Optional channel to notify when each slab is downloaded.
79
- /// This can be used to implement progress reporting.
80
- pub slab_downloaded: Option<mpsc::UnboundedSender<()>>,
81
- }
82
-
83
- impl Default for DownloadOptions {
84
- fn default() -> Self {
85
- Self {
86
- #[cfg(target_arch = "wasm32")]
87
- max_inflight: 2, // Browsers can't handle many concurrent WebTransport connections (no connection pooling)
88
- #[cfg(not(target_arch = "wasm32"))]
89
- max_inflight: 20,
90
- offset: 0,
91
- length: None,
92
- slab_downloaded: None,
93
- }
94
- }
95
- }
96
-
97
- #[derive(Clone)]
98
- pub(crate) struct Downloader {
99
- account_key: Arc<PrivateKey>,
100
- hosts: Hosts,
101
- transport: Arc<dyn RHP4Client>,
102
- #[cfg(target_arch = "wasm32")]
103
- default_max_inflight: usize,
104
- }
105
-
106
- struct SectorDownloadTask {
107
- sector: Sector,
108
- offset: u64,
109
- length: u64,
110
- index: usize,
111
- }
112
-
113
- impl Downloader {
114
- // helper to pair a sector with its erasure-coded index.
115
- // Required because [FuturesUnordered.push] does not
116
- // preserve ordering and doesn't play nice with closures.
117
- async fn try_download_sector(
118
- _permit: OwnedSemaphorePermit,
119
- transport: Arc<dyn RHP4Client>,
120
- account_key: Arc<PrivateKey>,
121
- task: SectorDownloadTask,
122
- ) -> Result<(usize, Vec<u8>), DownloadError> {
123
- let data = transport
124
- .read_sector(
125
- task.sector.host_key,
126
- &account_key,
127
- task.sector.root,
128
- task.offset as usize,
129
- task.length as usize,
130
- )
131
- .await?;
132
- Ok((task.index, data.to_vec()))
133
- }
134
-
135
- #[cfg(not(target_arch = "wasm32"))]
136
- pub fn new(hosts: Hosts, transport: Arc<dyn RHP4Client>, account_key: Arc<PrivateKey>) -> Self {
137
- Self {
138
- account_key,
139
- hosts,
140
- transport,
141
- }
142
- }
143
-
144
- #[cfg(target_arch = "wasm32")]
145
- pub fn new(
146
- hosts: Hosts,
147
- transport: Arc<dyn RHP4Client>,
148
- account_key: Arc<PrivateKey>,
149
- default_max_inflight: usize,
150
- ) -> Self {
151
- Self {
152
- account_key,
153
- hosts,
154
- transport,
155
- default_max_inflight,
156
- }
157
- }
158
-
159
- /// Returns default download options with the configured max_inflight.
160
- #[cfg(target_arch = "wasm32")]
161
- pub fn default_options(&self) -> DownloadOptions {
162
- DownloadOptions {
163
- max_inflight: self.default_max_inflight,
164
- ..Default::default()
165
- }
166
- }
167
-
168
- /// Downloads the shards of an erasure-coded slab.
169
- /// Successful shards will be decrypted using the
170
- /// encryption_key.
171
- ///
172
- /// offset and limit are the byte range to download
173
- /// from each sector.
174
- async fn download_slab_shards(
175
- &self,
176
- encryption_key: &EncryptionKey,
177
- sectors: &[Sector],
178
- min_shards: u8,
179
- offset: u64,
180
- length: u64,
181
- max_inflight: usize,
182
- ) -> Result<Vec<Option<Vec<u8>>>, DownloadError> {
183
- if sectors.len() < min_shards as usize {
184
- return Err(DownloadError::InvalidSlab(format!(
185
- "not enough sectors: have {}, need at least {}",
186
- sectors.len(),
187
- min_shards
188
- )));
189
- }
190
-
191
- let semaphore = Arc::new(Semaphore::new(max_inflight));
192
- let mut sectors = sectors
193
- .iter()
194
- .enumerate()
195
- .map(|(index, s)| SectorDownloadTask {
196
- sector: s.clone(),
197
- offset,
198
- length,
199
- index,
200
- })
201
- .collect::<Vec<_>>();
202
- self.hosts
203
- .prioritize(&mut sectors, |task| &task.sector.host_key);
204
- let total_shards = sectors.len();
205
- let mut sectors = VecDeque::from(sectors);
206
- let mut download_tasks = JoinSet::new();
207
- for _ in 0..min_shards {
208
- match sectors.pop_front() {
209
- Some(task) => {
210
- let permit = semaphore.clone().acquire_owned().await?;
211
- let transport = self.transport.clone();
212
- let account_key = self.account_key.clone();
213
- join_set_spawn!(download_tasks, Self::try_download_sector(
214
- permit,
215
- transport,
216
- account_key,
217
- task,
218
- ));
219
- }
220
- None => panic!("not enough sectors to satisfy min_shards"), // should be unreachable
221
- };
222
- }
223
-
224
- let mut successful: u8 = 0;
225
- let mut shards = vec![None; total_shards];
226
- let mut total_failures: usize = 0;
227
- const MAX_TOTAL_FAILURES: usize = 30; // Give up after 30 failed attempts total
228
-
229
- loop {
230
- tokio::select! {
231
- biased;
232
- Some(res) = download_tasks.join_next() => {
233
- match res? { // safe because tasks are never cancelled
234
- Ok((index, mut data)) => {
235
- let encryption_key = encryption_key.clone();
236
- #[cfg(not(target_arch = "wasm32"))]
237
- let data = spawn_blocking(move || {
238
- encrypt_shard(&encryption_key, index as u8, offset as usize, &mut data);
239
- data
240
- }).await?;
241
- #[cfg(target_arch = "wasm32")]
242
- let data = {
243
- encrypt_shard(&encryption_key, index as u8, offset as usize, &mut data);
244
- data
245
- };
246
- shards[index] = Some(data);
247
- successful += 1;
248
- if successful >= min_shards {
249
- return Ok(shards);
250
- }
251
- }
252
- Err(e) => {
253
- total_failures += 1;
254
- debug!("sector download failed ({total_failures}/{MAX_TOTAL_FAILURES}): {:?}", e);
255
-
256
- if total_failures >= MAX_TOTAL_FAILURES {
257
- return Err(DownloadError::NotEnoughShards(successful, min_shards));
258
- }
259
-
260
- let rem = min_shards.saturating_sub(successful);
261
- if rem == 0 {
262
- return Ok(shards); // sanity check
263
- } else if download_tasks.len() + sectors.len() < rem as usize {
264
- return Err(DownloadError::NotEnoughShards(successful, min_shards));
265
- } else if download_tasks.len() <= rem as usize && let Some(task) = sectors.pop_front() {
266
- let transport = self.transport.clone();
267
- let account_key = self.account_key.clone();
268
- let permit = semaphore.clone().acquire_owned().await?;
269
- // only spawn additional download tasks if there
270
- // are not enough to satisfy the required number
271
- // of shards. The sleep arm will handle slow
272
- // hosts.
273
- join_set_spawn!(download_tasks, Self::try_download_sector(
274
- permit,
275
- transport,
276
- account_key,
277
- task,
278
- ));
279
- }
280
- }
281
- }
282
- },
283
- _ = sleep(Duration::from_secs(1)) => {
284
- if let Ok(racer_permit) = semaphore.clone().try_acquire_owned()
285
- && let Some(task) = sectors.pop_front() {
286
- let transport = self.transport.clone();
287
- let account_key = self.account_key.clone();
288
- join_set_spawn!(download_tasks, Self::try_download_sector(
289
- racer_permit,
290
- transport,
291
- account_key,
292
- task,
293
- ));
294
- }
295
- }
296
- }
297
- }
298
- }
299
-
300
- /// Downloads the provided slabs and writes the decrypted data to the
301
- /// provided writer.
302
- pub async fn download<W: AsyncWriteExt + Unpin>(
303
- &self,
304
- w: &mut W,
305
- object: &Object,
306
- options: DownloadOptions,
307
- ) -> Result<(), DownloadError> {
308
- let mut w = object.writer(w, options.offset as usize);
309
- let mut offset = options.offset;
310
- let max_length = object.size();
311
- let mut length = options.length.unwrap_or(max_length);
312
- if offset > max_length || length == 0 {
313
- return Ok(());
314
- }
315
-
316
- for slab in object.slabs() {
317
- if length == 0 {
318
- break;
319
- }
320
-
321
- let slab_length = slab.length as u64;
322
- if offset >= slab_length {
323
- offset -= slab_length;
324
- continue;
325
- }
326
-
327
- // adjust slab range based on offset and length
328
- let slab_offset = slab.offset as u64 + offset;
329
- let slab_length = (slab_length - offset).min(length);
330
- offset = 0;
331
-
332
- // compute the sector aligned region to download
333
- let chunk_size = SEGMENT_SIZE as u64 * slab.min_shards as u64;
334
- let start = (slab_offset / chunk_size) * SEGMENT_SIZE as u64;
335
- let end = (slab_offset + slab_length).div_ceil(chunk_size) * SEGMENT_SIZE as u64;
336
- let shard_offset = start;
337
- let shard_length = end - start;
338
-
339
- let mut shards = self
340
- .download_slab_shards(
341
- &slab.encryption_key,
342
- &slab.sectors,
343
- slab.min_shards,
344
- shard_offset,
345
- shard_length,
346
- options.max_inflight,
347
- )
348
- .await?;
349
- let data_shards = slab.min_shards as usize;
350
- let parity_shards = slab.sectors.len() - slab.min_shards as usize;
351
- #[cfg(not(target_arch = "wasm32"))]
352
- let shards = spawn_blocking(move || -> Result<Vec<Option<Vec<u8>>>, DownloadError> {
353
- let rs = ErasureCoder::new(data_shards, parity_shards)?;
354
- rs.reconstruct_data_shards(&mut shards)?;
355
- Ok(shards)
356
- })
357
- .await??;
358
- #[cfg(target_arch = "wasm32")]
359
- let shards = {
360
- let rs = ErasureCoder::new(data_shards, parity_shards)?;
361
- rs.reconstruct_data_shards(&mut shards)?;
362
- shards
363
- };
364
- ErasureCoder::write_data_shards(
365
- &mut w,
366
- &shards[..data_shards],
367
- slab_offset as usize % (SEGMENT_SIZE * slab.min_shards as usize),
368
- slab_length as usize,
369
- )
370
- .await?;
371
- length -= slab_length;
372
- if let Some(ref tx) = options.slab_downloaded {
373
- let _ = tx.send(());
374
- }
375
- }
376
- w.flush().await?;
377
- Ok(())
378
- }
379
- }