Skip to content

Commit 67a17ee

Browse files
committed
fix: revert back uploads to uint8array and stream
1 parent d7b9661 commit 67a17ee

14 files changed

Lines changed: 140 additions & 127 deletions

File tree

docs/src/content/docs/developer-guides/storage/storage-context.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,9 @@ const storageContext = await synapse.storage.createContext({
231231
const llmModel = "sonnnet-4.5";
232232
const conversationId = "1234567890";
233233

234-
const data = new Blob(["Deep research on decentralization..."])
234+
const data = new TextEncoder().encode("Deep research on decentralization...")
235235

236-
const preflight = await storageContext.preflightUpload(data.size);
236+
const preflight = await storageContext.preflightUpload(data.length);
237237

238238
console.log("Estimated costs:", preflight.estimatedCost);
239239
console.log("Allowance sufficient:", preflight.allowanceCheck.sufficient);

docs/src/content/docs/developer-guides/storage/storage-operations.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ import { privateKeyToAccount } from 'viem/accounts'
5353

5454
const synapse = Synapse.create({ account: privateKeyToAccount('0x...') });
5555
// ---cut---
56-
const file = new Blob([new Uint8Array([1, 2, 3, 4, 5])]);
56+
const data = new Uint8Array([1, 2, 3, 4, 5]);
5757

58-
const result = await synapse.storage.upload(file);
58+
const result = await synapse.storage.upload(data);
5959
const downloaded = await synapse.storage.download(result.pieceCid);
6060

6161
console.log("Uploaded:", result.pieceCid);
@@ -71,7 +71,7 @@ Add metadata to organize uploads and enable faster data set reuse - SDK will reu
7171
import { Synapse } from "@filoz/synapse-sdk";
7272
import { privateKeyToAccount } from 'viem/accounts'
7373

74-
const data = new Blob([new Uint8Array([1, 2, 3, 4, 5])]);
74+
const data = new Uint8Array([1, 2, 3, 4, 5]);
7575
const synapse = Synapse.create({ account: privateKeyToAccount('0x...') });
7676
// ---cut---
7777
const context = await synapse.storage.createContext({

docs/src/content/docs/getting-started/index.mdx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,12 @@ async function main() {
9090
console.log(`✅ USDFC deposit and Warm Storage service approval successful!`);
9191

9292
// 3) Upload
93-
const file = new Blob([
93+
const file = new TextEncoder().encode(
9494
`🚀 Welcome to decentralized storage on Filecoin Onchain Cloud!
9595
Your data is safe here.
9696
🌍 You need to make sure to meet the minimum size
9797
requirement of 127 bytes per upload.`
98-
]);
98+
);
9999
const { pieceCid, size } = await synapse.storage.upload(file)
100100
console.log(`✅ Upload complete!`);
101101
console.log(`PieceCID: ${pieceCid}`);
@@ -179,12 +179,12 @@ import { privateKeyToAccount } from 'viem/accounts'
179179
const synapse = Synapse.create({ account: privateKeyToAccount('0x...') })
180180
// ---cut---
181181
// Upload data - SDK automatically selects provider and creates data set if needed
182-
const file = new Blob([
182+
const file = new TextEncoder().encode(
183183
`🚀 Welcome to decentralized storage on Filecoin Onchain Cloud!
184184
Your data is safe here.
185185
🌍 You need to make sure to meet the minimum size
186186
requirement of 127 bytes per upload.`
187-
]);
187+
);
188188
const { pieceCid } = await synapse.storage.upload(file);
189189

190190
// Download data from any provider that has it
@@ -226,12 +226,12 @@ For more control over provider selection and data set management:
226226
import { Synapse } from "@filoz/synapse-sdk";
227227
import { privateKeyToAccount } from 'viem/accounts'
228228
const synapse = Synapse.create({ account: privateKeyToAccount('0x...') })
229-
const file = new Blob([
229+
const file = new TextEncoder().encode(
230230
`🚀 Welcome to decentralized storage on Filecoin Onchain Cloud!
231231
Your data is safe here.
232232
🌍 You need to make sure to meet the minimum size
233233
requirement of 127 bytes per upload.`
234-
]);
234+
);
235235
// ---cut---
236236
// Create a storage context with specific provider
237237
const context = await synapse.storage.createContext({

examples/cli/src/commands/upload.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1+
import { open } from 'node:fs/promises'
12
import path from 'node:path'
23
import * as p from '@clack/prompts'
34
import { createPieceUrlPDP } from '@filoz/synapse-core/utils'
45
import { Synapse } from '@filoz/synapse-sdk'
5-
import { openLazyFile } from '@remix-run/fs'
66
import { type Command, command } from 'cleye'
77
import { privateKeyClient } from '../client.ts'
88
import { globalFlags } from '../flags.ts'
@@ -41,7 +41,7 @@ export const upload: Command = command(
4141

4242
const filePath = argv._.requiredPath
4343
const absolutePath = path.resolve(filePath)
44-
const file = openLazyFile(absolutePath)
44+
const fileHandle = await open(absolutePath)
4545

4646
try {
4747
const synapse = new Synapse({
@@ -63,7 +63,8 @@ export const upload: Command = command(
6363
},
6464
})
6565

66-
await context.upload(file, {
66+
const data = fileHandle.readableWebStream()
67+
await context.upload(data, {
6768
metadata: {
6869
name: path.basename(absolutePath),
6970
},

packages/synapse-core/src/mocks/pdp.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export interface PieceMetadataCapture {
2525
}
2626

2727
export function createAndAddPiecesHandler(txHash: Hex, options: PDPMockOptions = {}) {
28-
const baseUrl = options.baseUrl ?? 'http://pdp.local'
28+
const baseUrl = options.baseUrl ?? 'https://pdp.example.com'
2929
return http.post(`${baseUrl}/pdp/data-sets/create-and-add`, () => {
3030
return new HttpResponse(null, {
3131
status: 201,
@@ -49,7 +49,7 @@ export function dataSetCreationStatusHandler(
4949
},
5050
options: PDPMockOptions = {}
5151
) {
52-
const baseUrl = options.baseUrl ?? 'http://pdp.local'
52+
const baseUrl = options.baseUrl ?? 'https://pdp.example.com'
5353

5454
return http.get(`${baseUrl}/pdp/data-sets/created/:txHash`, ({ params }) => {
5555
if (params.txHash !== txHash) {
@@ -69,7 +69,7 @@ export function pieceAdditionStatusHandler(
6969
response: any,
7070
options: PDPMockOptions = {}
7171
) {
72-
const baseUrl = options.baseUrl ?? 'http://pdp.local'
72+
const baseUrl = options.baseUrl ?? 'https://pdp.example.com'
7373

7474
return http.get(`${baseUrl}/pdp/data-sets/:id/pieces/added/:txHash`, ({ params }) => {
7575
if (params.id !== dataSetId.toString() || params.txHash !== txHash) {
@@ -84,7 +84,7 @@ export function pieceAdditionStatusHandler(
8484
* Creates a handler for finding pieces
8585
*/
8686
export function findPieceHandler(pieceCid: string, found: boolean, options: PDPMockOptions = {}) {
87-
const baseUrl = options.baseUrl ?? 'http://pdp.local'
87+
const baseUrl = options.baseUrl ?? 'https://pdp.example.com'
8888

8989
return http.get(`${baseUrl}/pdp/piece`, ({ request }) => {
9090
const url = new URL(request.url)
@@ -102,7 +102,7 @@ export function findPieceHandler(pieceCid: string, found: boolean, options: PDPM
102102
}
103103

104104
export function findAnyPieceHandler(found: boolean, options: PDPMockOptions = {}) {
105-
const baseUrl = options.baseUrl ?? 'http://pdp.local'
105+
const baseUrl = options.baseUrl ?? 'https://pdp.example.com'
106106
return http.get(`${baseUrl}/pdp/piece`, ({ request }) => {
107107
const url = new URL(request.url)
108108
const queryCid = url.searchParams.get('pieceCid')
@@ -119,7 +119,7 @@ export function findAnyPieceHandler(found: boolean, options: PDPMockOptions = {}
119119
* Returns a UUID for 201, or a CID for 200
120120
*/
121121
export function postPieceHandler(pieceCid: string, uuid?: string, options: PDPMockOptions = {}) {
122-
const baseUrl = options.baseUrl ?? 'http://pdp.local'
122+
const baseUrl = options.baseUrl ?? 'https://pdp.example.com'
123123
return http.post<Record<string, never>, { pieceCid: string }>(`${baseUrl}/pdp/piece`, async ({ request }) => {
124124
const body = await request.json()
125125
assert(body != null, 'Body should be defined')
@@ -142,7 +142,7 @@ export function postPieceHandler(pieceCid: string, uuid?: string, options: PDPMo
142142
}
143143

144144
export function uploadPieceHandler(uuid: string, options: PDPMockOptions = {}) {
145-
const baseUrl = options.baseUrl ?? 'http://pdp.local'
145+
const baseUrl = options.baseUrl ?? 'https://pdp.example.com'
146146
return http.put(`${baseUrl}/pdp/piece/upload/${uuid}`, async () => {
147147
return HttpResponse.text('No Content', {
148148
status: 204,
@@ -156,7 +156,7 @@ export function uploadPieceHandler(uuid: string, options: PDPMockOptions = {}) {
156156
* Note: This endpoint doesn't require a request body
157157
*/
158158
export function postPieceUploadsHandler(uuid: string, options: PDPMockOptions = {}) {
159-
const baseUrl = options.baseUrl ?? 'http://pdp.local'
159+
const baseUrl = options.baseUrl ?? 'https://pdp.example.com'
160160
return http.post(`${baseUrl}/pdp/piece/uploads`, async () => {
161161
// Create upload session, return UUID in Location header
162162
return HttpResponse.text('Created', {
@@ -173,7 +173,7 @@ export function postPieceUploadsHandler(uuid: string, options: PDPMockOptions =
173173
* PUT /pdp/piece/uploads/:uuid - streams piece data
174174
*/
175175
export function uploadPieceStreamingHandler(uuid: string, options: PDPMockOptions = {}) {
176-
const baseUrl = options.baseUrl ?? 'http://pdp.local'
176+
const baseUrl = options.baseUrl ?? 'https://pdp.example.com'
177177
return http.put(`${baseUrl}/pdp/piece/uploads/${uuid}`, async ({ request }) => {
178178
await request.arrayBuffer()
179179
return HttpResponse.text('No Content', {
@@ -187,7 +187,7 @@ export function uploadPieceStreamingHandler(uuid: string, options: PDPMockOption
187187
* POST /pdp/piece/uploads/:uuid - finalize with PieceCID
188188
*/
189189
export function finalizePieceUploadHandler(uuid: string, expectedPieceCid?: string, options: PDPMockOptions = {}) {
190-
const baseUrl = options.baseUrl ?? 'http://pdp.local'
190+
const baseUrl = options.baseUrl ?? 'https://pdp.example.com'
191191
return http.post<{ uuid: string }, { pieceCid: string }>(
192192
`${baseUrl}/pdp/piece/uploads/${uuid}`,
193193
async ({ request }) => {
@@ -211,7 +211,7 @@ export function finalizePieceUploadHandler(uuid: string, expectedPieceCid?: stri
211211
* Returns array of handlers for: POST /pdp/piece/uploads, PUT /pdp/piece/uploads/:uuid, POST /pdp/piece/uploads/:uuid
212212
*/
213213
export function streamingUploadHandlers(options: PDPMockOptions = {}) {
214-
const baseUrl = options.baseUrl ?? 'http://pdp.local'
214+
const baseUrl = options.baseUrl ?? 'https://pdp.example.com'
215215
let uploadCounter = 0
216216

217217
return [
@@ -288,7 +288,7 @@ export function createDataSetWithMetadataCapture(
288288
captureCallback: (metadata: MetadataCapture) => void,
289289
options: PDPMockOptions = {}
290290
) {
291-
const baseUrl = options.baseUrl ?? 'http://pdp.local'
291+
const baseUrl = options.baseUrl ?? 'https://pdp.example.com'
292292

293293
return http.post(`${baseUrl}/pdp/data-sets`, async ({ request }) => {
294294
const body = (await request.json()) as any
@@ -330,7 +330,7 @@ export function addPiecesWithMetadataCapture(
330330
captureCallback: (metadata: PieceMetadataCapture) => void,
331331
options: PDPMockOptions = {}
332332
) {
333-
const baseUrl = options.baseUrl ?? 'http://pdp.local'
333+
const baseUrl = options.baseUrl ?? 'https://pdp.example.com'
334334

335335
return http.post<{ id: string }, addPieces.RequestBody>(
336336
`${baseUrl}/pdp/data-sets/:id/pieces`,

packages/synapse-core/src/sp/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export * from './add-pieces.ts'
1414
export * from './data-sets.ts'
1515
export * from './get-data-set.ts'
1616
export * from './schedule-piece-deletion.ts'
17-
export type { deletePiece } from './sp.ts'
17+
export type { deletePiece, UploadPieceStreamingData } from './sp.ts'
1818
export { downloadPiece, findPiece, ping, uploadPiece, uploadPieceStreaming } from './sp.ts'
1919
export * from './upload.ts'
2020
export * from './wait-for-add-pieces.ts'

packages/synapse-core/src/sp/sp.ts

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import * as Piece from '../piece.ts'
1717
import type * as TypedData from '../typed-data/index.ts'
1818
import { RETRY_CONSTANTS, SIZE_CONSTANTS } from '../utils/constants.ts'
1919
import { createPieceUrlPDP } from '../utils/piece-url.ts'
20+
import { isUint8Array } from '../utils/streams.ts'
2021

2122
export namespace createDataSet {
2223
/**
@@ -235,12 +236,13 @@ export async function uploadPiece(options: uploadPiece.OptionsType): Promise<voi
235236
}
236237
}
237238

239+
export type UploadPieceStreamingData = Uint8Array | ReadableStream | import('node:stream/web').ReadableStream
238240
export namespace uploadPieceStreaming {
239241
export type OptionsType = {
240242
/** The service URL of the PDP API. */
241243
serviceURL: string
242244
/** The data to upload. */
243-
data: Blob
245+
data: UploadPieceStreamingData
244246
/** The size of the data. If defined, it will be used to set the Content-Length header. */
245247
size?: number
246248
/** The progress callback. */
@@ -273,9 +275,6 @@ export namespace uploadPieceStreaming {
273275
export async function uploadPieceStreaming(
274276
options: uploadPieceStreaming.OptionsType
275277
): Promise<uploadPieceStreaming.OutputType> {
276-
if (options.data.size < SIZE_CONSTANTS.MIN_UPLOAD_SIZE || options.data.size > SIZE_CONSTANTS.MAX_UPLOAD_SIZE) {
277-
throw new InvalidUploadSizeError(options.data.size)
278-
}
279278
// Create upload session (POST /pdp/piece/uploads)
280279
const createResponse = await request.post(new URL('pdp/piece/uploads', options.serviceURL), {
281280
timeout: RETRY_CONSTANTS.MAX_RETRY_TIME,
@@ -316,20 +315,46 @@ export async function uploadPieceStreaming(
316315
getPieceCID = result.getPieceCID
317316
}
318317

319-
const dataStream = options.data.stream()
318+
const dataStream = isUint8Array(options.data)
319+
? new Blob([options.data as Uint8Array<ArrayBuffer>]).stream()
320+
: (options.data as ReadableStream) // ReadableStream types dont match between browsers and Node.js
321+
322+
const size = isUint8Array(options.data) ? options.data.length : options.size
320323

321324
// Add size tracking and progress reporting
322325
let bytesUploaded = 0
323-
const trackingStream = new TransformStream<Uint8Array, Uint8Array>({
326+
const trackingStream = new TransformStream<unknown, Uint8Array>({
324327
transform(chunk, controller) {
325-
bytesUploaded += chunk.length
328+
let bytes: Uint8Array | undefined
329+
330+
if (isUint8Array(chunk)) {
331+
bytes = chunk
332+
} else if (ArrayBuffer.isView(chunk)) {
333+
bytes = new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength)
334+
} else {
335+
controller.error('Invalid chunk type only Uint8Array and TypedArray are supported')
336+
return
337+
}
338+
339+
bytesUploaded += bytes.length
340+
341+
if (bytesUploaded > SIZE_CONSTANTS.MAX_UPLOAD_SIZE) {
342+
controller.error(new InvalidUploadSizeError(bytesUploaded))
343+
return
344+
}
326345

327346
// Report progress if callback provided
328347
if (options.onProgress) {
329348
options.onProgress(bytesUploaded)
330349
}
331350

332-
controller.enqueue(chunk)
351+
controller.enqueue(bytes)
352+
},
353+
flush(controller) {
354+
if (bytesUploaded < SIZE_CONSTANTS.MIN_UPLOAD_SIZE) {
355+
controller.error(new InvalidUploadSizeError(bytesUploaded))
356+
return
357+
}
333358
},
334359
})
335360

@@ -341,7 +366,7 @@ export async function uploadPieceStreaming(
341366
// PUT /pdp/piece/uploads/{uuid} with streaming body
342367
const headers: Record<string, string> = {
343368
'Content-Type': 'application/octet-stream',
344-
'Content-Length': options.data.size.toString(),
369+
...(size != null ? { 'Content-Length': size.toString() } : {}),
345370
}
346371

347372
const uploadResponse = await request.put(new URL(`pdp/piece/uploads/${uploadUuid}`, options.serviceURL), {

packages/synapse-core/src/utils/streams.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,13 @@ export async function* uint8ArrayToAsyncIterable(
111111
yield data.subarray(i, i + chunkSize)
112112
}
113113
}
114+
115+
/**
116+
* Check if value is Uint8Array
117+
*
118+
* @param value - The value to check
119+
* @returns True if it's a Uint8Array
120+
*/
121+
export function isUint8Array(value: unknown): value is Uint8Array {
122+
return value instanceof Uint8Array || (ArrayBuffer.isView(value) && value.constructor.name === 'Uint8Array')
123+
}

0 commit comments

Comments
 (0)