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.
- package/package.json +2 -6
- package/template/AGENTS.md +143 -0
- package/template/CLAUDE.md +25 -46
- package/template/README.md +6 -12
- package/template/_gitignore +0 -1
- package/template/dist/assets/index-BEylY2j7.css +1 -0
- package/template/dist/assets/index-CnYqArKN.js +8741 -0
- package/template/dist/assets/sia_bg-BTOHUC1A.wasm +0 -0
- package/template/dist/assets/slab-download-worker-DhW6ZBJs.js +2 -0
- package/template/dist/assets/slab-upload-worker-B2uSB2iY.js +2 -0
- package/template/dist/index.html +13 -0
- package/template/e2e/smoke.spec.ts +20 -0
- package/template/index.html +0 -1
- package/template/package.json +2 -2
- package/template/playwright.config.ts +13 -0
- package/template/src/components/Navbar.tsx +3 -3
- package/template/src/components/auth/ApproveScreen.tsx +10 -13
- package/template/src/components/auth/AuthFlow.tsx +13 -10
- package/template/src/components/auth/ConnectScreen.tsx +2 -3
- package/template/src/components/auth/RecoveryScreen.tsx +7 -7
- package/template/src/components/upload/UploadZone.tsx +192 -74
- package/template/src/index.css +14 -4
- package/template/src/stores/auth.ts +6 -12
- package/template/test-results/.last-run.json +4 -0
- package/template/tsconfig.app.json +1 -1
- package/template/tsconfig.node.json +0 -1
- package/template/vite.config.ts +2 -3
- package/template/rust/README.md +0 -16
- package/template/rust/sia-sdk-rs/.changeset/added_cancel_function_to_cancel_inflight_packed_uploads.md +0 -6
- package/template/rust/sia-sdk-rs/.changeset/check_if_we_have_enough_hosts_prior_to_encoding_in_upload_slabs.md +0 -16
- package/template/rust/sia-sdk-rs/.changeset/fix_slab_length_in_packed_object.md +0 -5
- package/template/rust/sia-sdk-rs/.changeset/fix_upload_racing_race_conditon.md +0 -13
- package/template/rust/sia-sdk-rs/.changeset/improved_parallelism_of_packed_uploads.md +0 -5
- package/template/rust/sia-sdk-rs/.changeset/progress_callback_will_now_be_called_as_expected_for_packed_uploads.md +0 -5
- package/template/rust/sia-sdk-rs/.github/dependabot.yml +0 -10
- package/template/rust/sia-sdk-rs/.github/workflows/main.yml +0 -36
- package/template/rust/sia-sdk-rs/.github/workflows/prepare-release.yml +0 -34
- package/template/rust/sia-sdk-rs/.github/workflows/release.yml +0 -30
- package/template/rust/sia-sdk-rs/.rustfmt.toml +0 -4
- package/template/rust/sia-sdk-rs/Cargo.lock +0 -4127
- package/template/rust/sia-sdk-rs/Cargo.toml +0 -3
- package/template/rust/sia-sdk-rs/LICENSE +0 -21
- package/template/rust/sia-sdk-rs/README.md +0 -30
- package/template/rust/sia-sdk-rs/indexd/CHANGELOG.md +0 -79
- package/template/rust/sia-sdk-rs/indexd/Cargo.toml +0 -79
- package/template/rust/sia-sdk-rs/indexd/benches/upload.rs +0 -258
- package/template/rust/sia-sdk-rs/indexd/src/app_client.rs +0 -1710
- package/template/rust/sia-sdk-rs/indexd/src/builder.rs +0 -354
- package/template/rust/sia-sdk-rs/indexd/src/download.rs +0 -379
- package/template/rust/sia-sdk-rs/indexd/src/hosts.rs +0 -659
- package/template/rust/sia-sdk-rs/indexd/src/lib.rs +0 -827
- package/template/rust/sia-sdk-rs/indexd/src/mock.rs +0 -162
- package/template/rust/sia-sdk-rs/indexd/src/object_encryption.rs +0 -125
- package/template/rust/sia-sdk-rs/indexd/src/quic.rs +0 -575
- package/template/rust/sia-sdk-rs/indexd/src/rhp4.rs +0 -52
- package/template/rust/sia-sdk-rs/indexd/src/slabs.rs +0 -497
- package/template/rust/sia-sdk-rs/indexd/src/upload.rs +0 -629
- package/template/rust/sia-sdk-rs/indexd/src/wasm_time.rs +0 -41
- package/template/rust/sia-sdk-rs/indexd/src/web_transport.rs +0 -398
- package/template/rust/sia-sdk-rs/indexd_ffi/CHANGELOG.md +0 -76
- package/template/rust/sia-sdk-rs/indexd_ffi/Cargo.toml +0 -47
- package/template/rust/sia-sdk-rs/indexd_ffi/examples/python/README.md +0 -10
- package/template/rust/sia-sdk-rs/indexd_ffi/examples/python/example.py +0 -130
- package/template/rust/sia-sdk-rs/indexd_ffi/src/bin/uniffi-bindgen.rs +0 -3
- package/template/rust/sia-sdk-rs/indexd_ffi/src/builder.rs +0 -377
- package/template/rust/sia-sdk-rs/indexd_ffi/src/io.rs +0 -155
- package/template/rust/sia-sdk-rs/indexd_ffi/src/lib.rs +0 -1039
- package/template/rust/sia-sdk-rs/indexd_ffi/src/logging.rs +0 -58
- package/template/rust/sia-sdk-rs/indexd_ffi/src/tls.rs +0 -23
- package/template/rust/sia-sdk-rs/indexd_wasm/Cargo.toml +0 -33
- package/template/rust/sia-sdk-rs/indexd_wasm/src/lib.rs +0 -818
- package/template/rust/sia-sdk-rs/knope.toml +0 -54
- package/template/rust/sia-sdk-rs/sia_derive/CHANGELOG.md +0 -38
- package/template/rust/sia-sdk-rs/sia_derive/Cargo.toml +0 -19
- package/template/rust/sia-sdk-rs/sia_derive/src/lib.rs +0 -278
- package/template/rust/sia-sdk-rs/sia_sdk/CHANGELOG.md +0 -91
- package/template/rust/sia-sdk-rs/sia_sdk/Cargo.toml +0 -59
- package/template/rust/sia-sdk-rs/sia_sdk/benches/merkle_root.rs +0 -12
- package/template/rust/sia-sdk-rs/sia_sdk/src/blake2.rs +0 -22
- package/template/rust/sia-sdk-rs/sia_sdk/src/consensus.rs +0 -767
- package/template/rust/sia-sdk-rs/sia_sdk/src/encoding/v1.rs +0 -257
- package/template/rust/sia-sdk-rs/sia_sdk/src/encoding/v2.rs +0 -291
- package/template/rust/sia-sdk-rs/sia_sdk/src/encoding.rs +0 -26
- package/template/rust/sia-sdk-rs/sia_sdk/src/encoding_async/v2.rs +0 -367
- package/template/rust/sia-sdk-rs/sia_sdk/src/encoding_async.rs +0 -6
- package/template/rust/sia-sdk-rs/sia_sdk/src/encryption.rs +0 -303
- package/template/rust/sia-sdk-rs/sia_sdk/src/erasure_coding.rs +0 -347
- package/template/rust/sia-sdk-rs/sia_sdk/src/lib.rs +0 -15
- package/template/rust/sia-sdk-rs/sia_sdk/src/macros.rs +0 -435
- package/template/rust/sia-sdk-rs/sia_sdk/src/merkle.rs +0 -112
- package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/merkle.rs +0 -357
- package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/rpc.rs +0 -1507
- package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/types.rs +0 -146
- package/template/rust/sia-sdk-rs/sia_sdk/src/rhp.rs +0 -7
- package/template/rust/sia-sdk-rs/sia_sdk/src/seed.rs +0 -278
- package/template/rust/sia-sdk-rs/sia_sdk/src/signing.rs +0 -236
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/common.rs +0 -677
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/currency.rs +0 -450
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/specifier.rs +0 -110
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/spendpolicy.rs +0 -778
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/utils.rs +0 -117
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/v1.rs +0 -1737
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/v2.rs +0 -1726
- package/template/rust/sia-sdk-rs/sia_sdk/src/types/work.rs +0 -59
- package/template/rust/sia-sdk-rs/sia_sdk/src/types.rs +0 -16
- package/template/scripts/setup-rust.js +0 -29
- package/template/src/lib/format.ts +0 -35
- package/template/src/lib/hex.ts +0 -13
- package/template/src/lib/sdk.ts +0 -25
- package/template/src/lib/wasm-env.ts +0 -5
- package/template/wasm/indexd_wasm/indexd_wasm.d.ts +0 -309
- package/template/wasm/indexd_wasm/indexd_wasm.js +0 -1507
- package/template/wasm/indexd_wasm/indexd_wasm_bg.wasm +0 -0
- package/template/wasm/indexd_wasm/package.json +0 -31
|
@@ -1,19 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type DownloadProgress,
|
|
3
|
+
decodeMetadata,
|
|
4
|
+
type PinnedObject,
|
|
5
|
+
toHex,
|
|
6
|
+
type UploadProgress,
|
|
7
|
+
} from '@siafoundation/sia'
|
|
1
8
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
2
9
|
import { APP_KEY } from '../../lib/constants'
|
|
3
|
-
import { type FileMetadata, formatBytes } from '../../lib/format'
|
|
4
|
-
import { toHex } from '../../lib/hex'
|
|
5
10
|
import { useAuthStore } from '../../stores/auth'
|
|
6
11
|
import { DevNote } from '../DevNote'
|
|
7
12
|
|
|
13
|
+
type FileMetadata = {
|
|
14
|
+
name: string
|
|
15
|
+
type: string
|
|
16
|
+
size: number
|
|
17
|
+
hash: string
|
|
18
|
+
createdAt?: number
|
|
19
|
+
updatedAt?: number
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function formatBytes(bytes: number): string {
|
|
23
|
+
if (bytes === 0) return '0 B'
|
|
24
|
+
const k = 1024
|
|
25
|
+
const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
|
|
26
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
27
|
+
return `${parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`
|
|
28
|
+
}
|
|
29
|
+
|
|
8
30
|
type UploadedFile = {
|
|
9
31
|
id: string
|
|
10
32
|
metadata: FileMetadata
|
|
33
|
+
object: PinnedObject
|
|
11
34
|
}
|
|
12
35
|
|
|
13
|
-
type
|
|
36
|
+
type ActiveUpload = {
|
|
14
37
|
fileName: string
|
|
15
|
-
|
|
16
|
-
total: number
|
|
38
|
+
progress: UploadProgress
|
|
17
39
|
}
|
|
18
40
|
|
|
19
41
|
// Detect unconfigured template placeholder. The string is split so the
|
|
@@ -21,61 +43,65 @@ type UploadProgress = {
|
|
|
21
43
|
const isPlaceholderKey = APP_KEY.startsWith('{' + '{')
|
|
22
44
|
|
|
23
45
|
export function UploadZone() {
|
|
24
|
-
const
|
|
46
|
+
const client = useAuthStore((s) => s.client)
|
|
25
47
|
const [files, setFiles] = useState<UploadedFile[]>([])
|
|
26
48
|
const [uploading, setUploading] = useState(false)
|
|
27
|
-
const [
|
|
49
|
+
const [activeUpload, setActiveUpload] = useState<ActiveUpload | null>(null)
|
|
28
50
|
const [dragOver, setDragOver] = useState(false)
|
|
29
51
|
const [error, setError] = useState<string | null>(null)
|
|
52
|
+
const [downloading, setDownloading] = useState<string | null>(null)
|
|
53
|
+
const [downloadProgress, setDownloadProgress] =
|
|
54
|
+
useState<DownloadProgress | null>(null)
|
|
30
55
|
const fileInputRef = useRef<HTMLInputElement>(null)
|
|
31
56
|
|
|
32
57
|
const loadFiles = useCallback(async () => {
|
|
33
|
-
if (!
|
|
58
|
+
if (!client) return
|
|
34
59
|
try {
|
|
35
|
-
const events = await
|
|
60
|
+
const events = await client.objectEvents(null, 100)
|
|
36
61
|
const loaded: UploadedFile[] = []
|
|
37
62
|
for (const event of events) {
|
|
38
63
|
if (event.deleted || !event.object) continue
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
new TextDecoder().decode(metaBytes),
|
|
43
|
-
) as FileMetadata
|
|
44
|
-
loaded.push({ id: event.id, metadata: meta })
|
|
45
|
-
} catch {
|
|
46
|
-
// skip objects with invalid metadata
|
|
64
|
+
const meta = decodeMetadata(event.object.metadata()) as FileMetadata
|
|
65
|
+
if (meta.name) {
|
|
66
|
+
loaded.push({ id: event.id, metadata: meta, object: event.object })
|
|
47
67
|
}
|
|
48
68
|
}
|
|
49
69
|
setFiles(loaded)
|
|
50
70
|
} catch (e) {
|
|
51
71
|
console.error('Failed to load files:', e)
|
|
52
72
|
}
|
|
53
|
-
}, [
|
|
73
|
+
}, [client])
|
|
54
74
|
|
|
55
75
|
useEffect(() => {
|
|
56
76
|
loadFiles()
|
|
57
77
|
}, [loadFiles])
|
|
58
78
|
|
|
59
79
|
async function uploadFile(file: File) {
|
|
60
|
-
if (!
|
|
80
|
+
if (!client) return
|
|
61
81
|
setUploading(true)
|
|
62
82
|
setError(null)
|
|
63
|
-
|
|
83
|
+
setActiveUpload({
|
|
84
|
+
fileName: file.name,
|
|
85
|
+
progress: {
|
|
86
|
+
phase: 'connecting',
|
|
87
|
+
slabsComplete: 0,
|
|
88
|
+
slabsTotal: 0,
|
|
89
|
+
shardsComplete: 0,
|
|
90
|
+
shardsTotal: 0,
|
|
91
|
+
},
|
|
92
|
+
})
|
|
64
93
|
|
|
65
94
|
try {
|
|
66
|
-
const arrayBuffer = await file.arrayBuffer()
|
|
67
|
-
const data = new Uint8Array(arrayBuffer)
|
|
68
|
-
|
|
69
95
|
// Compute SHA-256 hash for metadata
|
|
70
|
-
const hashBuffer = await crypto.subtle.digest(
|
|
96
|
+
const hashBuffer = await crypto.subtle.digest(
|
|
97
|
+
'SHA-256',
|
|
98
|
+
await file.arrayBuffer(),
|
|
99
|
+
)
|
|
71
100
|
const hash = toHex(new Uint8Array(hashBuffer))
|
|
72
101
|
|
|
73
|
-
const pinnedObject = await
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
setProgress({ fileName: file.name, current, total })
|
|
77
|
-
},
|
|
78
|
-
)
|
|
102
|
+
const pinnedObject = await client.upload(file, (p) => {
|
|
103
|
+
setActiveUpload({ fileName: file.name, progress: p })
|
|
104
|
+
})
|
|
79
105
|
|
|
80
106
|
const metadata: FileMetadata = {
|
|
81
107
|
name: file.name,
|
|
@@ -88,15 +114,41 @@ export function UploadZone() {
|
|
|
88
114
|
pinnedObject.updateMetadata(
|
|
89
115
|
new TextEncoder().encode(JSON.stringify(metadata)),
|
|
90
116
|
)
|
|
91
|
-
await
|
|
92
|
-
await
|
|
117
|
+
await client.pinObject(pinnedObject)
|
|
118
|
+
await client.updateObjectMetadata(pinnedObject)
|
|
93
119
|
|
|
94
|
-
setFiles((prev) => [
|
|
120
|
+
setFiles((prev) => [
|
|
121
|
+
{ id: pinnedObject.id(), metadata, object: pinnedObject },
|
|
122
|
+
...prev,
|
|
123
|
+
])
|
|
95
124
|
} catch (e) {
|
|
96
125
|
setError(e instanceof Error ? e.message : 'Upload failed')
|
|
97
126
|
} finally {
|
|
98
127
|
setUploading(false)
|
|
99
|
-
|
|
128
|
+
setActiveUpload(null)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function downloadFile(file: UploadedFile) {
|
|
133
|
+
if (!client) return
|
|
134
|
+
setDownloading(file.id)
|
|
135
|
+
setDownloadProgress(null)
|
|
136
|
+
try {
|
|
137
|
+
const data = await client.download(file.object, (p) => {
|
|
138
|
+
setDownloadProgress(p)
|
|
139
|
+
})
|
|
140
|
+
const blob = new Blob([data as BlobPart], { type: file.metadata.type })
|
|
141
|
+
const url = URL.createObjectURL(blob)
|
|
142
|
+
const a = document.createElement('a')
|
|
143
|
+
a.href = url
|
|
144
|
+
a.download = file.metadata.name
|
|
145
|
+
a.click()
|
|
146
|
+
URL.revokeObjectURL(url)
|
|
147
|
+
} catch (e) {
|
|
148
|
+
setError(e instanceof Error ? e.message : 'Download failed')
|
|
149
|
+
} finally {
|
|
150
|
+
setDownloading(null)
|
|
151
|
+
setDownloadProgress(null)
|
|
100
152
|
}
|
|
101
153
|
}
|
|
102
154
|
|
|
@@ -114,12 +166,31 @@ export function UploadZone() {
|
|
|
114
166
|
}
|
|
115
167
|
}
|
|
116
168
|
|
|
117
|
-
const
|
|
169
|
+
const progress = activeUpload?.progress
|
|
170
|
+
const isIndeterminate =
|
|
171
|
+
progress &&
|
|
172
|
+
(progress.phase === 'connecting' ||
|
|
173
|
+
progress.phase === 'assembling' ||
|
|
174
|
+
progress.phase === 'pinning')
|
|
118
175
|
const progressPercent =
|
|
119
|
-
progress && progress.
|
|
120
|
-
? Math.round((progress.
|
|
176
|
+
progress && progress.slabsTotal > 0
|
|
177
|
+
? Math.round((progress.slabsComplete / progress.slabsTotal) * 100)
|
|
121
178
|
: 0
|
|
122
179
|
|
|
180
|
+
function progressLabel(): string {
|
|
181
|
+
if (!progress) return ''
|
|
182
|
+
switch (progress.phase) {
|
|
183
|
+
case 'connecting':
|
|
184
|
+
return 'Connecting to hosts...'
|
|
185
|
+
case 'uploading':
|
|
186
|
+
return `${progress.slabsComplete}/${progress.slabsTotal} slabs \u00B7 ${progressPercent}%`
|
|
187
|
+
case 'assembling':
|
|
188
|
+
return 'Assembling object...'
|
|
189
|
+
case 'pinning':
|
|
190
|
+
return 'Pinning object...'
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
123
194
|
return (
|
|
124
195
|
<div className="flex-1 p-6 space-y-5 max-w-5xl mx-auto w-full">
|
|
125
196
|
{/* Dev notes — remove these when shipping */}
|
|
@@ -134,16 +205,17 @@ export function UploadZone() {
|
|
|
134
205
|
</DevNote>
|
|
135
206
|
)}
|
|
136
207
|
|
|
137
|
-
<DevNote title="Upload
|
|
208
|
+
<DevNote title="Upload & Download">
|
|
138
209
|
<p>
|
|
139
|
-
<code className="text-amber-300">sdk.uploadWithProgress()</code>{' '}
|
|
140
|
-
encrypts, erasure-codes, and uploads directly to Sia hosts. Metadata
|
|
141
|
-
is attached via{' '}
|
|
142
210
|
<code className="text-amber-300">
|
|
143
|
-
|
|
211
|
+
client.upload(file, onProgress)
|
|
144
212
|
</code>{' '}
|
|
145
|
-
|
|
146
|
-
|
|
213
|
+
encrypts, erasure-codes, and uploads to Sia hosts using parallel Web
|
|
214
|
+
Workers.{' '}
|
|
215
|
+
<code className="text-amber-300">
|
|
216
|
+
client.download(object, onProgress)
|
|
217
|
+
</code>{' '}
|
|
218
|
+
retrieves and decrypts files the same way.
|
|
147
219
|
</p>
|
|
148
220
|
</DevNote>
|
|
149
221
|
|
|
@@ -191,18 +263,18 @@ export function UploadZone() {
|
|
|
191
263
|
}}
|
|
192
264
|
/>
|
|
193
265
|
|
|
194
|
-
{
|
|
266
|
+
{activeUpload ? (
|
|
195
267
|
<div className="space-y-4">
|
|
196
268
|
<p className="text-neutral-300 text-sm">
|
|
197
|
-
{
|
|
269
|
+
{progress?.phase === 'connecting' ? (
|
|
198
270
|
<>
|
|
199
271
|
Preparing upload for{' '}
|
|
200
|
-
<span className="text-white">{
|
|
272
|
+
<span className="text-white">{activeUpload.fileName}</span>
|
|
201
273
|
</>
|
|
202
274
|
) : (
|
|
203
275
|
<>
|
|
204
276
|
Uploading{' '}
|
|
205
|
-
<span className="text-white">{
|
|
277
|
+
<span className="text-white">{activeUpload.fileName}</span>
|
|
206
278
|
</>
|
|
207
279
|
)}
|
|
208
280
|
</p>
|
|
@@ -217,9 +289,7 @@ export function UploadZone() {
|
|
|
217
289
|
)}
|
|
218
290
|
</div>
|
|
219
291
|
<p className="text-neutral-600 text-xs font-mono">
|
|
220
|
-
{
|
|
221
|
-
? 'Preparing upload...'
|
|
222
|
-
: `${progress.current}/${progress.total} shards \u00B7 ${progressPercent}%`}
|
|
292
|
+
{progressLabel()}
|
|
223
293
|
</p>
|
|
224
294
|
</div>
|
|
225
295
|
) : (
|
|
@@ -252,30 +322,78 @@ export function UploadZone() {
|
|
|
252
322
|
{files.length} file{files.length !== 1 ? 's' : ''}
|
|
253
323
|
</h2>
|
|
254
324
|
<div className="divide-y divide-neutral-800/60">
|
|
255
|
-
{files.map((file) =>
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
<p className="text-sm text-neutral-200 truncate">
|
|
262
|
-
{file.metadata.name}
|
|
263
|
-
</p>
|
|
264
|
-
<p className="text-xs text-neutral-600 mt-0.5">
|
|
265
|
-
{formatBytes(file.metadata.size)}
|
|
266
|
-
{file.metadata.type !== 'application/octet-stream' && (
|
|
267
|
-
<span> · {file.metadata.type}</span>
|
|
268
|
-
)}
|
|
269
|
-
</p>
|
|
270
|
-
</div>
|
|
271
|
-
<span
|
|
272
|
-
className="text-[11px] text-neutral-700 font-mono shrink-0 group-hover:text-neutral-500 transition-colors"
|
|
273
|
-
title={file.metadata.hash}
|
|
325
|
+
{files.map((file) => {
|
|
326
|
+
const isDownloading = downloading === file.id
|
|
327
|
+
return (
|
|
328
|
+
<div
|
|
329
|
+
key={file.id}
|
|
330
|
+
className="flex items-center justify-between py-3 group"
|
|
274
331
|
>
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
332
|
+
<div className="flex-1 min-w-0 mr-4">
|
|
333
|
+
<p className="text-sm text-neutral-200 truncate">
|
|
334
|
+
{file.metadata.name}
|
|
335
|
+
</p>
|
|
336
|
+
<p className="text-xs text-neutral-600 mt-0.5">
|
|
337
|
+
{formatBytes(file.metadata.size)}
|
|
338
|
+
{file.metadata.type !== 'application/octet-stream' && (
|
|
339
|
+
<span> · {file.metadata.type}</span>
|
|
340
|
+
)}
|
|
341
|
+
{isDownloading && downloadProgress && (
|
|
342
|
+
<span>
|
|
343
|
+
{' · '}
|
|
344
|
+
{downloadProgress.phase === 'downloading'
|
|
345
|
+
? `${downloadProgress.slabsComplete}/${downloadProgress.slabsTotal} slabs`
|
|
346
|
+
: downloadProgress.phase === 'connecting'
|
|
347
|
+
? 'Connecting...'
|
|
348
|
+
: 'Assembling...'}
|
|
349
|
+
</span>
|
|
350
|
+
)}
|
|
351
|
+
</p>
|
|
352
|
+
</div>
|
|
353
|
+
<div className="flex items-center gap-3 shrink-0">
|
|
354
|
+
<button
|
|
355
|
+
type="button"
|
|
356
|
+
onClick={() => downloadFile(file)}
|
|
357
|
+
disabled={downloading !== null}
|
|
358
|
+
className="text-xs text-neutral-600 hover:text-neutral-300 disabled:opacity-30 disabled:cursor-default transition-colors"
|
|
359
|
+
title="Download"
|
|
360
|
+
>
|
|
361
|
+
{isDownloading ? (
|
|
362
|
+
<svg
|
|
363
|
+
className="w-4 h-4 animate-spin"
|
|
364
|
+
viewBox="0 0 24 24"
|
|
365
|
+
fill="none"
|
|
366
|
+
stroke="currentColor"
|
|
367
|
+
strokeWidth="2"
|
|
368
|
+
aria-hidden="true"
|
|
369
|
+
>
|
|
370
|
+
<circle cx="12" cy="12" r="10" strokeOpacity="0.25" />
|
|
371
|
+
<path d="M12 2a10 10 0 019.17 6" />
|
|
372
|
+
</svg>
|
|
373
|
+
) : (
|
|
374
|
+
<svg
|
|
375
|
+
className="w-4 h-4"
|
|
376
|
+
viewBox="0 0 24 24"
|
|
377
|
+
fill="none"
|
|
378
|
+
stroke="currentColor"
|
|
379
|
+
strokeWidth="1.5"
|
|
380
|
+
aria-hidden="true"
|
|
381
|
+
>
|
|
382
|
+
<path d="M12 4v12m0 0l-4-4m4 4l4-4" />
|
|
383
|
+
<path d="M20 16v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2" />
|
|
384
|
+
</svg>
|
|
385
|
+
)}
|
|
386
|
+
</button>
|
|
387
|
+
<span
|
|
388
|
+
className="text-[11px] text-neutral-700 font-mono group-hover:text-neutral-500 transition-colors"
|
|
389
|
+
title={file.metadata.hash}
|
|
390
|
+
>
|
|
391
|
+
{file.metadata.hash.slice(0, 8)}...
|
|
392
|
+
</span>
|
|
393
|
+
</div>
|
|
394
|
+
</div>
|
|
395
|
+
)
|
|
396
|
+
})}
|
|
279
397
|
</div>
|
|
280
398
|
</div>
|
|
281
399
|
)}
|
package/template/src/index.css
CHANGED
|
@@ -9,13 +9,23 @@ body {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
@keyframes fade-in {
|
|
12
|
-
from {
|
|
13
|
-
|
|
12
|
+
from {
|
|
13
|
+
opacity: 0;
|
|
14
|
+
transform: translateY(8px);
|
|
15
|
+
}
|
|
16
|
+
to {
|
|
17
|
+
opacity: 1;
|
|
18
|
+
transform: translateY(0);
|
|
19
|
+
}
|
|
14
20
|
}
|
|
15
21
|
|
|
16
22
|
@keyframes indeterminate {
|
|
17
|
-
0% {
|
|
18
|
-
|
|
23
|
+
0% {
|
|
24
|
+
transform: translateX(-100%);
|
|
25
|
+
}
|
|
26
|
+
100% {
|
|
27
|
+
transform: translateX(400%);
|
|
28
|
+
}
|
|
19
29
|
}
|
|
20
30
|
|
|
21
31
|
.animate-fade-in {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import type { SiaClient } from '@siafoundation/sia'
|
|
1
2
|
import { create } from 'zustand'
|
|
2
3
|
import { persist } from 'zustand/middleware'
|
|
3
|
-
import type { Builder, SDK } from '../lib/sdk'
|
|
4
4
|
|
|
5
5
|
export type AuthStep =
|
|
6
6
|
| 'loading'
|
|
@@ -9,20 +9,14 @@ export type AuthStep =
|
|
|
9
9
|
| 'recovery'
|
|
10
10
|
| 'connected'
|
|
11
11
|
|
|
12
|
-
export function applyBalancedPreset(builder: Builder) {
|
|
13
|
-
builder.withMaxPriceFetches(5)
|
|
14
|
-
builder.withMaxDownloads(10)
|
|
15
|
-
builder.withMaxUploads(8)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
12
|
type AuthState = {
|
|
19
|
-
|
|
13
|
+
client: SiaClient | null
|
|
20
14
|
storedKeyHex: string | null
|
|
21
15
|
indexerUrl: string
|
|
22
16
|
step: AuthStep
|
|
23
17
|
error: string | null
|
|
24
18
|
approvalUrl: string | null
|
|
25
|
-
|
|
19
|
+
setClient: (client: SiaClient) => void
|
|
26
20
|
setStep: (step: AuthStep) => void
|
|
27
21
|
setError: (error: string | null) => void
|
|
28
22
|
setStoredKeyHex: (hex: string) => void
|
|
@@ -34,13 +28,13 @@ type AuthState = {
|
|
|
34
28
|
export const useAuthStore = create<AuthState>()(
|
|
35
29
|
persist(
|
|
36
30
|
(set) => ({
|
|
37
|
-
|
|
31
|
+
client: null,
|
|
38
32
|
storedKeyHex: null,
|
|
39
33
|
indexerUrl: '',
|
|
40
34
|
step: 'loading',
|
|
41
35
|
error: null,
|
|
42
36
|
approvalUrl: null,
|
|
43
|
-
|
|
37
|
+
setClient: (client) => set({ client, step: 'connected', error: null }),
|
|
44
38
|
setStep: (step) => set({ step, error: null }),
|
|
45
39
|
setError: (error) => set({ error }),
|
|
46
40
|
setStoredKeyHex: (hex) => set({ storedKeyHex: hex }),
|
|
@@ -48,7 +42,7 @@ export const useAuthStore = create<AuthState>()(
|
|
|
48
42
|
setApprovalUrl: (url) => set({ approvalUrl: url }),
|
|
49
43
|
reset: () =>
|
|
50
44
|
set({
|
|
51
|
-
|
|
45
|
+
client: null,
|
|
52
46
|
storedKeyHex: null,
|
|
53
47
|
step: 'loading',
|
|
54
48
|
error: null,
|
package/template/vite.config.ts
CHANGED
|
@@ -7,12 +7,11 @@ import wasm from 'vite-plugin-wasm'
|
|
|
7
7
|
export default defineConfig({
|
|
8
8
|
plugins: [react(), tailwindcss(), wasm(), topLevelAwait()],
|
|
9
9
|
optimizeDeps: {
|
|
10
|
-
exclude: ['
|
|
10
|
+
exclude: ['@siafoundation/sia', 'sia-wasm'],
|
|
11
11
|
},
|
|
12
12
|
resolve: {
|
|
13
13
|
alias: {
|
|
14
|
-
|
|
15
|
-
env: '/src/lib/wasm-env.ts',
|
|
14
|
+
'sia-wasm': '@siafoundation/sia/wasm',
|
|
16
15
|
},
|
|
17
16
|
},
|
|
18
17
|
})
|
package/template/rust/README.md
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# Rust SDK Source
|
|
2
|
-
|
|
3
|
-
The Rust SDK source (`sia-sdk-rs/`) is cloned automatically on `bun install` from [alexfreska/sia-sdk-rs](https://github.com/alexfreska/sia-sdk-rs) (branch `alex/wasm-experimental`).
|
|
4
|
-
|
|
5
|
-
The pre-built WASM in `../wasm/` is what the app actually uses at runtime. The Rust source is only needed if you want to rebuild the WASM module.
|
|
6
|
-
|
|
7
|
-
## Rebuilding WASM
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
cd sia-sdk-rs
|
|
11
|
-
cargo install wasm-pack
|
|
12
|
-
wasm-pack build indexd_wasm --target web --out-dir pkg
|
|
13
|
-
cp indexd_wasm/pkg/indexd_wasm_bg.wasm ../../wasm/indexd_wasm/
|
|
14
|
-
cp indexd_wasm/pkg/indexd_wasm.js ../../wasm/indexd_wasm/
|
|
15
|
-
cp indexd_wasm/pkg/indexd_wasm.d.ts ../../wasm/indexd_wasm/
|
|
16
|
-
```
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
sia_sdk: patch
|
|
3
|
-
indexd: patch
|
|
4
|
-
indexd_ffi: patch
|
|
5
|
-
sia_sdk_derive: patch
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Check if we have enough hosts prior to encoding in upload_slabs
|
|
9
|
-
|
|
10
|
-
#261 by @Alrighttt
|
|
11
|
-
|
|
12
|
-
Fixes https://github.com/SiaFoundation/sia-sdk-rs/issues/251
|
|
13
|
-
|
|
14
|
-
- Added an `available_for_upload` method that returns the amount of known hosts marked `good_for_upload`.
|
|
15
|
-
- Added a check in `upload_slabs` that verifies we have enough good hosts prior to encoding any data.
|
|
16
|
-
- Adds a variant to `QueueError` for `upload_slabs`'s new failure case. This enables testing for this new case specifically.
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
indexd: patch
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Fix upload racing race conditon
|
|
6
|
-
|
|
7
|
-
#258 by @Alrighttt
|
|
8
|
-
|
|
9
|
-
This fixes a race condition in the upload logic that could happen when the amount of healthy hosts is nearly the same as the amount of shards. This could happen when the racing mechanism was triggered prior to all of the initial shards being assigned a host. The slow hosts would be consumed from the HostQueue without completing the upload. This would cause a latter shard to hit a QueueError::NoMoreHosts error.
|
|
10
|
-
|
|
11
|
-
This changes the upload behavior so that each shard has a host assigned before any upload begins.
|
|
12
|
-
|
|
13
|
-
A `set_slow_hosts` method was added to the `MockRHP4Client` to allow easily testing these conditions. This mimics a similar mechanism from the Go SDK.
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
name: Main
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches:
|
|
6
|
-
- master
|
|
7
|
-
pull_request:
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
test:
|
|
11
|
-
runs-on: tenki-standard-autoscale
|
|
12
|
-
steps:
|
|
13
|
-
- uses: actions/checkout@v4
|
|
14
|
-
- name: Setup Environment
|
|
15
|
-
run: |
|
|
16
|
-
rustup update stable
|
|
17
|
-
rustup toolchain install nightly
|
|
18
|
-
rustup target add wasm32-unknown-unknown
|
|
19
|
-
rustup component add rustfmt --toolchain nightly
|
|
20
|
-
- name: Build for wasm
|
|
21
|
-
# Check if the library target compiles. This will still allow for using
|
|
22
|
-
# non-wasm functionality in tests and benchmarks but guarantees that
|
|
23
|
-
# consumers of the library can use it to generate wasm bindings.
|
|
24
|
-
run: cargo check --target wasm32-unknown-unknown --lib --package sia_sdk
|
|
25
|
-
- name: Rustfmt
|
|
26
|
-
run: cargo +nightly fmt --all -- --check
|
|
27
|
-
- name: Clippy
|
|
28
|
-
run: cargo clippy -- -D warnings
|
|
29
|
-
- name: Build docs
|
|
30
|
-
run: cargo doc --all-features --no-deps
|
|
31
|
-
env:
|
|
32
|
-
RUSTDOCFLAGS: "-D warnings"
|
|
33
|
-
- name: Build benches
|
|
34
|
-
run: cargo build --benches --features=mock
|
|
35
|
-
- name: Test
|
|
36
|
-
run: cargo test
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
name: Prepare Release
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
workflow_dispatch:
|
|
5
|
-
push:
|
|
6
|
-
branches:
|
|
7
|
-
- master
|
|
8
|
-
|
|
9
|
-
permissions:
|
|
10
|
-
contents: write
|
|
11
|
-
pull-requests: write
|
|
12
|
-
|
|
13
|
-
jobs:
|
|
14
|
-
prepare-release:
|
|
15
|
-
if: "!contains(github.event.head_commit.message, 'chore: prepare releases')" # Skip merges from releases
|
|
16
|
-
runs-on: tenki-standard-autoscale
|
|
17
|
-
steps:
|
|
18
|
-
- uses: actions/checkout@v4
|
|
19
|
-
with:
|
|
20
|
-
fetch-depth: 0
|
|
21
|
-
- name: Setup Knope
|
|
22
|
-
uses: knope-dev/action@407e9ef7c272d2dd53a4e71e39a7839e29933c48
|
|
23
|
-
- name: Prepare Release
|
|
24
|
-
run: knope prepare-release --verbose
|
|
25
|
-
- name: Create Pull Request
|
|
26
|
-
uses: peter-evans/create-pull-request@v7
|
|
27
|
-
with:
|
|
28
|
-
delete-branch: true
|
|
29
|
-
commit-message: "chore: prepare releases"
|
|
30
|
-
title: Release Versions
|
|
31
|
-
base: master
|
|
32
|
-
branch: release
|
|
33
|
-
body: |
|
|
34
|
-
This PR was automatically created to prepare the release versions. Close the PR when you are ready to release. See the changelog for more details.
|