@ardrive/turbo-sdk 1.26.0 → 1.27.0-alpha.1
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/bundles/web.bundle.min.js +244 -82
- package/lib/cjs/common/events.js +4 -4
- package/lib/cjs/common/upload.js +8 -7
- package/lib/cjs/utils/readableStream.js +100 -2
- package/lib/cjs/version.js +1 -1
- package/lib/cjs/web/index.js +1 -0
- package/lib/cjs/web/signer.js +128 -56
- package/lib/esm/common/events.js +4 -4
- package/lib/esm/common/upload.js +8 -7
- package/lib/esm/utils/readableStream.js +95 -0
- package/lib/esm/version.js +1 -1
- package/lib/esm/web/index.js +1 -0
- package/lib/esm/web/signer.js +126 -57
- package/lib/types/common/events.d.ts.map +1 -1
- package/lib/types/common/upload.d.ts.map +1 -1
- package/lib/types/utils/readableStream.d.ts +6 -0
- package/lib/types/utils/readableStream.d.ts.map +1 -1
- package/lib/types/version.d.ts +1 -1
- package/lib/types/version.d.ts.map +1 -1
- package/lib/types/web/index.d.ts +1 -0
- package/lib/types/web/index.d.ts.map +1 -1
- package/lib/types/web/signer.d.ts +16 -6
- package/lib/types/web/signer.d.ts.map +1 -1
- package/package.json +2 -2
- package/lib/cjs/common/events.test.js +0 -470
- package/lib/esm/common/events.test.js +0 -468
- package/lib/types/common/events.test.d.ts +0 -2
- package/lib/types/common/events.test.d.ts.map +0 -1
package/lib/cjs/web/index.js
CHANGED
package/lib/cjs/web/signer.js
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.TurboWebArweaveSigner = exports.HexSolanaSigner = exports.EthereumSigner = exports.ArweaveSigner = exports.ArconnectSigner = void 0;
|
3
|
+
exports.readableStreamToAsyncIterable = exports.TurboWebArweaveSigner = exports.HexSolanaSigner = exports.EthereumSigner = exports.ArweaveSigner = exports.ArconnectSigner = void 0;
|
4
|
+
exports.streamSignerReadableStream = streamSignerReadableStream;
|
5
|
+
exports.isAsyncIterable = isAsyncIterable;
|
4
6
|
/**
|
5
7
|
* Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
|
6
8
|
*
|
@@ -46,61 +48,21 @@ class TurboWebArweaveSigner extends signer_js_1.TurboDataItemAbstractSigner {
|
|
46
48
|
await this.setPublicKey();
|
47
49
|
// Create signing emitter if events are provided
|
48
50
|
const fileSize = fileSizeFactory();
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
emitter?.emit('signing-progress', {
|
65
|
-
processedBytes: Math.floor(fileSize / 2),
|
66
|
-
totalBytes: fileSize,
|
67
|
-
});
|
68
|
-
let signedDataItem;
|
69
|
-
this.logger.debug('Signing data item...');
|
70
|
-
if (this.signer instanceof arbundles_1.ArconnectSigner) {
|
71
|
-
this.logger.debug('Arconnect signer detected, signing with Arconnect signData Item API...');
|
72
|
-
const sign = Buffer.from(await this.signer['signer'].signDataItem({
|
73
|
-
data: Uint8Array.from(buffer),
|
74
|
-
tags: dataItemOpts?.tags,
|
75
|
-
target: dataItemOpts?.target,
|
76
|
-
anchor: dataItemOpts?.anchor,
|
77
|
-
}));
|
78
|
-
signedDataItem = new arbundles_1.DataItem(sign);
|
79
|
-
}
|
80
|
-
else {
|
81
|
-
signedDataItem = (0, arbundles_1.createData)(Uint8Array.from(buffer), this.signer, dataItemOpts);
|
82
|
-
await signedDataItem.sign(this.signer);
|
83
|
-
}
|
84
|
-
// emit last progress event (100%)
|
85
|
-
emitter?.emit('signing-progress', {
|
86
|
-
processedBytes: fileSize,
|
87
|
-
totalBytes: fileSize,
|
88
|
-
});
|
89
|
-
// emit completion event
|
90
|
-
emitter?.emit('signing-success');
|
91
|
-
this.logger.debug('Successfully signed data item...');
|
92
|
-
return {
|
93
|
-
// while this returns a Buffer - it needs to match our return type for uploading
|
94
|
-
dataItemStreamFactory: () => signedDataItem.getRaw(),
|
95
|
-
dataItemSizeFactory: () => signedDataItem.getRaw().length,
|
96
|
-
};
|
97
|
-
}
|
98
|
-
catch (error) {
|
99
|
-
// If we have a signing emitter, emit error
|
100
|
-
// TODO: create a SigningError class and throw that instead of the generic Error
|
101
|
-
emitter?.emit('signing-error', error);
|
102
|
-
throw error;
|
103
|
-
}
|
51
|
+
this.logger.debug('Signing data item...');
|
52
|
+
const { signedDataItemFactory, signedDataItemSize } = await streamSignerReadableStream({
|
53
|
+
streamFactory: (0, readableStream_js_1.createUint8ArrayReadableStreamFactory)({
|
54
|
+
data: fileStreamFactory(),
|
55
|
+
}),
|
56
|
+
signer: this.signer,
|
57
|
+
dataItemOpts,
|
58
|
+
fileSize,
|
59
|
+
emitter,
|
60
|
+
});
|
61
|
+
this.logger.debug('Successfully signed data item...');
|
62
|
+
return {
|
63
|
+
dataItemStreamFactory: signedDataItemFactory,
|
64
|
+
dataItemSizeFactory: () => signedDataItemSize,
|
65
|
+
};
|
104
66
|
}
|
105
67
|
async generateSignedRequestHeaders() {
|
106
68
|
await this.setPublicKey();
|
@@ -112,3 +74,113 @@ class TurboWebArweaveSigner extends signer_js_1.TurboDataItemAbstractSigner {
|
|
112
74
|
}
|
113
75
|
}
|
114
76
|
exports.TurboWebArweaveSigner = TurboWebArweaveSigner;
|
77
|
+
const readableStreamToAsyncIterable = (stream) => ({
|
78
|
+
async *[Symbol.asyncIterator]() {
|
79
|
+
const reader = stream.getReader();
|
80
|
+
try {
|
81
|
+
while (true) {
|
82
|
+
const { done, value } = await reader.read();
|
83
|
+
if (done)
|
84
|
+
break;
|
85
|
+
if (value !== undefined)
|
86
|
+
yield Buffer.from(value);
|
87
|
+
}
|
88
|
+
}
|
89
|
+
finally {
|
90
|
+
reader.releaseLock();
|
91
|
+
}
|
92
|
+
},
|
93
|
+
});
|
94
|
+
exports.readableStreamToAsyncIterable = readableStreamToAsyncIterable;
|
95
|
+
async function streamSignerReadableStream({ streamFactory, signer, dataItemOpts, fileSize, emitter, }) {
|
96
|
+
try {
|
97
|
+
const header = (0, arbundles_1.createData)('', signer, dataItemOpts);
|
98
|
+
const headerSize = header.getRaw().byteLength;
|
99
|
+
const totalDataItemSizeWithHeader = fileSize + headerSize;
|
100
|
+
const [stream1, stream2] = streamFactory().tee();
|
101
|
+
const reader1 = stream1.getReader();
|
102
|
+
let bytesProcessed = 0;
|
103
|
+
const eventingStream = new ReadableStream({
|
104
|
+
start() {
|
105
|
+
// technically this should be emitted after each DeepHashChunk be we cannot access that stage, so we emit it here instead
|
106
|
+
bytesProcessed = headerSize;
|
107
|
+
emitter?.emit('signing-progress', {
|
108
|
+
processedBytes: bytesProcessed,
|
109
|
+
totalBytes: totalDataItemSizeWithHeader,
|
110
|
+
});
|
111
|
+
},
|
112
|
+
async pull(controller) {
|
113
|
+
const { done, value } = await reader1.read();
|
114
|
+
if (done) {
|
115
|
+
controller.close();
|
116
|
+
return;
|
117
|
+
}
|
118
|
+
bytesProcessed += value.byteLength;
|
119
|
+
controller.enqueue(value);
|
120
|
+
emitter?.emit('signing-progress', {
|
121
|
+
processedBytes: bytesProcessed,
|
122
|
+
totalBytes: totalDataItemSizeWithHeader,
|
123
|
+
});
|
124
|
+
},
|
125
|
+
cancel() {
|
126
|
+
reader1.cancel();
|
127
|
+
},
|
128
|
+
});
|
129
|
+
// create a readable that emits signing events as bytes are pulled through using the first stream from .tee()
|
130
|
+
const asyncIterableReadableStream = (0, exports.readableStreamToAsyncIterable)(eventingStream);
|
131
|
+
// provide that ReadableStream with events to deep hash, so as it pulls bytes through events get emitted
|
132
|
+
const parts = [
|
133
|
+
(0, arbundles_1.stringToBuffer)('dataitem'),
|
134
|
+
(0, arbundles_1.stringToBuffer)('1'),
|
135
|
+
(0, arbundles_1.stringToBuffer)(header.signatureType.toString()),
|
136
|
+
Uint8Array.from(header.rawOwner),
|
137
|
+
Uint8Array.from(header.rawTarget),
|
138
|
+
Uint8Array.from(header.rawAnchor),
|
139
|
+
Uint8Array.from(header.rawTags),
|
140
|
+
asyncIterableReadableStream,
|
141
|
+
];
|
142
|
+
const hash = await (0, arbundles_1.deepHash)(parts);
|
143
|
+
const sigBytes = Buffer.from(await signer.sign(hash));
|
144
|
+
emitter?.emit('signing-success');
|
145
|
+
header.setSignature(sigBytes);
|
146
|
+
const headerBytes = header.getRaw();
|
147
|
+
const signedDataItemFactory = () => {
|
148
|
+
const reader = stream2.getReader();
|
149
|
+
return new ReadableStream({
|
150
|
+
start(controller) {
|
151
|
+
controller.enqueue(Uint8Array.from(headerBytes));
|
152
|
+
bytesProcessed += headerBytes.byteLength;
|
153
|
+
},
|
154
|
+
async pull(controller) {
|
155
|
+
try {
|
156
|
+
const { done, value } = await reader.read();
|
157
|
+
if (done) {
|
158
|
+
controller.close();
|
159
|
+
return;
|
160
|
+
}
|
161
|
+
controller.enqueue(value);
|
162
|
+
}
|
163
|
+
catch (error) {
|
164
|
+
controller.error(error);
|
165
|
+
}
|
166
|
+
},
|
167
|
+
cancel() {
|
168
|
+
reader.cancel();
|
169
|
+
},
|
170
|
+
});
|
171
|
+
};
|
172
|
+
return {
|
173
|
+
signedDataItemSize: totalDataItemSizeWithHeader,
|
174
|
+
signedDataItemFactory,
|
175
|
+
};
|
176
|
+
}
|
177
|
+
catch (error) {
|
178
|
+
emitter?.emit('signing-error', error);
|
179
|
+
throw error;
|
180
|
+
}
|
181
|
+
}
|
182
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
183
|
+
function isAsyncIterable(data) {
|
184
|
+
return (typeof data[Symbol.asyncIterator] ===
|
185
|
+
'function');
|
186
|
+
}
|
package/lib/esm/common/events.js
CHANGED
@@ -40,7 +40,7 @@ function createReadableStreamWithEvents({ data, dataSize, emitter, eventNamesMap
|
|
40
40
|
? data
|
41
41
|
: new ReadableStream({
|
42
42
|
start: (controller) => {
|
43
|
-
controller.enqueue(data);
|
43
|
+
controller.enqueue(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
|
44
44
|
controller.close();
|
45
45
|
},
|
46
46
|
});
|
@@ -58,12 +58,12 @@ function createReadableStreamWithEvents({ data, dataSize, emitter, eventNamesMap
|
|
58
58
|
controller.close();
|
59
59
|
return;
|
60
60
|
}
|
61
|
-
processedBytes += value.
|
61
|
+
processedBytes += value.byteLength;
|
62
62
|
emitter.emit(eventNamesMap['on-progress'], {
|
63
63
|
processedBytes,
|
64
64
|
totalBytes: dataSize,
|
65
65
|
});
|
66
|
-
controller.enqueue(value);
|
66
|
+
controller.enqueue(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
|
67
67
|
}
|
68
68
|
catch (error) {
|
69
69
|
emitter.emit(eventNamesMap['on-error'], error);
|
@@ -119,7 +119,7 @@ function createReadableWithEvents({ data, dataSize, emitter, eventNamesMap, }) {
|
|
119
119
|
let processedBytes = 0;
|
120
120
|
existingStream.on('data', (chunk) => {
|
121
121
|
eventingStream.write(chunk);
|
122
|
-
processedBytes += chunk.
|
122
|
+
processedBytes += chunk.byteLength;
|
123
123
|
emitter.emit(eventNamesMap['on-progress'], {
|
124
124
|
processedBytes,
|
125
125
|
totalBytes: dataSize,
|
package/lib/esm/common/upload.js
CHANGED
@@ -41,19 +41,19 @@ export class TurboUnauthenticatedUploadService {
|
|
41
41
|
this.retryConfig = retryConfig;
|
42
42
|
}
|
43
43
|
async uploadSignedDataItem({ dataItemStreamFactory, dataItemSizeFactory, dataItemOpts, signal, events = {}, }) {
|
44
|
-
const
|
44
|
+
const dataItemSize = dataItemSizeFactory();
|
45
45
|
this.logger.debug('Uploading signed data item...');
|
46
46
|
// create the tapped stream with events
|
47
47
|
const emitter = new TurboEventEmitter(events);
|
48
48
|
// create the stream with upload events
|
49
49
|
const { stream: streamWithUploadEvents, resume } = createStreamWithUploadEvents({
|
50
50
|
data: dataItemStreamFactory(),
|
51
|
-
dataSize:
|
51
|
+
dataSize: dataItemSize,
|
52
52
|
emitter,
|
53
53
|
});
|
54
54
|
const headers = {
|
55
55
|
'content-type': 'application/octet-stream',
|
56
|
-
'content-length': `${
|
56
|
+
'content-length': `${dataItemSize}`,
|
57
57
|
};
|
58
58
|
if (dataItemOpts !== undefined && dataItemOpts.paidBy !== undefined) {
|
59
59
|
const paidBy = Array.isArray(dataItemOpts.paidBy)
|
@@ -136,11 +136,10 @@ export class TurboAuthenticatedBaseUploadService extends TurboUnauthenticatedUpl
|
|
136
136
|
if (signal?.aborted) {
|
137
137
|
throw new CanceledError();
|
138
138
|
}
|
139
|
+
// Now that we have the signed data item, we can upload it using the uploadSignedDataItem method
|
140
|
+
// which will create a new emitter with upload events. We await
|
141
|
+
// this result due to the wrapped retry logic of this method.
|
139
142
|
try {
|
140
|
-
this.logger.debug('Uploading signed data item...');
|
141
|
-
// Now that we have the signed data item, we can upload it using the uploadSignedDataItem method
|
142
|
-
// which will create a new emitter with upload events. We await
|
143
|
-
// this result due to the wrapped retry logic of this method.
|
144
143
|
const response = await this.uploadSignedDataItem({
|
145
144
|
dataItemStreamFactory,
|
146
145
|
dataItemSizeFactory,
|
@@ -246,6 +245,7 @@ export class TurboAuthenticatedBaseUploadService extends TurboUnauthenticatedUpl
|
|
246
245
|
};
|
247
246
|
try {
|
248
247
|
const result = await this.uploadFile({
|
248
|
+
// TODO: can fix this type by passing a class generic and specifying in the node/web abstracts which stream type to use
|
249
249
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
250
250
|
fileStreamFactory: () => this.getFileStreamForFile(file),
|
251
251
|
fileSizeFactory: () => this.getFileSize(file),
|
@@ -290,6 +290,7 @@ export class TurboAuthenticatedBaseUploadService extends TurboUnauthenticatedUpl
|
|
290
290
|
];
|
291
291
|
const manifestBuffer = Buffer.from(JSON.stringify(manifest));
|
292
292
|
const manifestResponse = await this.uploadFile({
|
293
|
+
// TODO: can fix this type by passing a class generic and specifying in the node/web abstracts which stream type to use
|
293
294
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
294
295
|
fileStreamFactory: () => this.createManifestStream(manifestBuffer),
|
295
296
|
fileSizeFactory: () => manifestBuffer.byteLength,
|
@@ -13,6 +13,7 @@
|
|
13
13
|
* See the License for the specific language governing permissions and
|
14
14
|
* limitations under the License.
|
15
15
|
*/
|
16
|
+
export const DEFAULT_STREAM_CHUNK_SIZE = 20 * 1024 * 1024; // 20mb
|
16
17
|
export async function readableStreamToBuffer({ stream, size, }) {
|
17
18
|
const reader = stream.getReader();
|
18
19
|
const buffer = Buffer.alloc(size);
|
@@ -28,3 +29,97 @@ export async function readableStreamToBuffer({ stream, size, }) {
|
|
28
29
|
}
|
29
30
|
return buffer;
|
30
31
|
}
|
32
|
+
export function ensureChunkedStream(input, maxChunkSize = DEFAULT_STREAM_CHUNK_SIZE) {
|
33
|
+
const reader = input.getReader();
|
34
|
+
let leftover = null;
|
35
|
+
return new ReadableStream({
|
36
|
+
async pull(controller) {
|
37
|
+
// If we have leftover from a previous large chunk, continue slicing it
|
38
|
+
if (leftover) {
|
39
|
+
const chunk = leftover.subarray(0, maxChunkSize);
|
40
|
+
leftover = leftover.subarray(chunk.length);
|
41
|
+
if (leftover.length === 0)
|
42
|
+
leftover = null;
|
43
|
+
controller.enqueue(chunk);
|
44
|
+
return;
|
45
|
+
}
|
46
|
+
const { value, done } = await reader.read();
|
47
|
+
if (done) {
|
48
|
+
controller.close();
|
49
|
+
return;
|
50
|
+
}
|
51
|
+
// Runtime check because ReadableStream defaults to <any> and can be abused
|
52
|
+
if (!(value instanceof Uint8Array)) {
|
53
|
+
throw new TypeError('Expected Uint8Array from source stream');
|
54
|
+
}
|
55
|
+
if (value.byteLength <= maxChunkSize) {
|
56
|
+
controller.enqueue(value);
|
57
|
+
}
|
58
|
+
else {
|
59
|
+
// Slice and enqueue one piece now, keep the rest
|
60
|
+
// subarray is the new view with the same buffer (not copy)
|
61
|
+
controller.enqueue(value.subarray(0, maxChunkSize));
|
62
|
+
leftover = value.subarray(maxChunkSize);
|
63
|
+
}
|
64
|
+
},
|
65
|
+
});
|
66
|
+
}
|
67
|
+
export function createUint8ArrayReadableStreamFactory({ data, maxChunkSize = DEFAULT_STREAM_CHUNK_SIZE, }) {
|
68
|
+
// Blob streams are already ReadableStream<Uint8Array>
|
69
|
+
if (data instanceof Blob) {
|
70
|
+
return () => ensureChunkedStream(data.stream());
|
71
|
+
}
|
72
|
+
// We need to handle the case where the data is a ReadableStream that is not a Uint8Array
|
73
|
+
// This is to ensure downstream code can handle the data as a Uint8Array
|
74
|
+
if (data instanceof ReadableStream) {
|
75
|
+
return () => {
|
76
|
+
const reader = data.getReader();
|
77
|
+
const stream = new ReadableStream({
|
78
|
+
async pull(controller) {
|
79
|
+
const { value, done } = await reader.read();
|
80
|
+
if (done) {
|
81
|
+
controller.close();
|
82
|
+
return;
|
83
|
+
}
|
84
|
+
if (ArrayBuffer.isView(value)) {
|
85
|
+
// specifying offset and length is required to ensure chunks remain within their slice of the buffer
|
86
|
+
controller.enqueue(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
|
87
|
+
}
|
88
|
+
else if (value instanceof ArrayBuffer ||
|
89
|
+
value instanceof SharedArrayBuffer) {
|
90
|
+
controller.enqueue(new Uint8Array(value));
|
91
|
+
}
|
92
|
+
else {
|
93
|
+
throw new TypeError('Unsupported chunk type in ReadableStream');
|
94
|
+
}
|
95
|
+
},
|
96
|
+
});
|
97
|
+
return ensureChunkedStream(stream, maxChunkSize);
|
98
|
+
};
|
99
|
+
}
|
100
|
+
return () => {
|
101
|
+
let uint8;
|
102
|
+
if (typeof data === 'string') {
|
103
|
+
uint8 = new TextEncoder().encode(data);
|
104
|
+
}
|
105
|
+
else if (ArrayBuffer.isView(data)) {
|
106
|
+
// In theory we could use the view directly, but that might allow other typed arrays like BigInt64Array to be used which could behave unexpectedly downstream
|
107
|
+
// specifying offset and length is required to ensure chunks remain within their slice of the buffer
|
108
|
+
uint8 = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
109
|
+
}
|
110
|
+
else if (data instanceof ArrayBuffer ||
|
111
|
+
data instanceof SharedArrayBuffer) {
|
112
|
+
uint8 = new Uint8Array(data);
|
113
|
+
}
|
114
|
+
else {
|
115
|
+
throw new TypeError('Unsupported input type for stream');
|
116
|
+
}
|
117
|
+
const stream = new ReadableStream({
|
118
|
+
start(controller) {
|
119
|
+
controller.enqueue(uint8);
|
120
|
+
controller.close();
|
121
|
+
},
|
122
|
+
});
|
123
|
+
return ensureChunkedStream(stream, maxChunkSize);
|
124
|
+
};
|
125
|
+
}
|
package/lib/esm/version.js
CHANGED
package/lib/esm/web/index.js
CHANGED
package/lib/esm/web/signer.js
CHANGED
@@ -13,9 +13,9 @@
|
|
13
13
|
* See the License for the specific language governing permissions and
|
14
14
|
* limitations under the License.
|
15
15
|
*/
|
16
|
-
import { ArconnectSigner, ArweaveSigner,
|
16
|
+
import { ArconnectSigner, ArweaveSigner, EthereumSigner, HexSolanaSigner, InjectedEthereumSigner, createData, deepHash, stringToBuffer, } from '@dha-team/arbundles';
|
17
17
|
import { TurboDataItemAbstractSigner } from '../common/signer.js';
|
18
|
-
import {
|
18
|
+
import { createUint8ArrayReadableStreamFactory } from '../utils/readableStream.js';
|
19
19
|
/**
|
20
20
|
* Utility exports to avoid clients having to install arbundles
|
21
21
|
*/
|
@@ -43,61 +43,21 @@ export class TurboWebArweaveSigner extends TurboDataItemAbstractSigner {
|
|
43
43
|
await this.setPublicKey();
|
44
44
|
// Create signing emitter if events are provided
|
45
45
|
const fileSize = fileSizeFactory();
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
emitter?.emit('signing-progress', {
|
62
|
-
processedBytes: Math.floor(fileSize / 2),
|
63
|
-
totalBytes: fileSize,
|
64
|
-
});
|
65
|
-
let signedDataItem;
|
66
|
-
this.logger.debug('Signing data item...');
|
67
|
-
if (this.signer instanceof ArconnectSigner) {
|
68
|
-
this.logger.debug('Arconnect signer detected, signing with Arconnect signData Item API...');
|
69
|
-
const sign = Buffer.from(await this.signer['signer'].signDataItem({
|
70
|
-
data: Uint8Array.from(buffer),
|
71
|
-
tags: dataItemOpts?.tags,
|
72
|
-
target: dataItemOpts?.target,
|
73
|
-
anchor: dataItemOpts?.anchor,
|
74
|
-
}));
|
75
|
-
signedDataItem = new DataItem(sign);
|
76
|
-
}
|
77
|
-
else {
|
78
|
-
signedDataItem = createData(Uint8Array.from(buffer), this.signer, dataItemOpts);
|
79
|
-
await signedDataItem.sign(this.signer);
|
80
|
-
}
|
81
|
-
// emit last progress event (100%)
|
82
|
-
emitter?.emit('signing-progress', {
|
83
|
-
processedBytes: fileSize,
|
84
|
-
totalBytes: fileSize,
|
85
|
-
});
|
86
|
-
// emit completion event
|
87
|
-
emitter?.emit('signing-success');
|
88
|
-
this.logger.debug('Successfully signed data item...');
|
89
|
-
return {
|
90
|
-
// while this returns a Buffer - it needs to match our return type for uploading
|
91
|
-
dataItemStreamFactory: () => signedDataItem.getRaw(),
|
92
|
-
dataItemSizeFactory: () => signedDataItem.getRaw().length,
|
93
|
-
};
|
94
|
-
}
|
95
|
-
catch (error) {
|
96
|
-
// If we have a signing emitter, emit error
|
97
|
-
// TODO: create a SigningError class and throw that instead of the generic Error
|
98
|
-
emitter?.emit('signing-error', error);
|
99
|
-
throw error;
|
100
|
-
}
|
46
|
+
this.logger.debug('Signing data item...');
|
47
|
+
const { signedDataItemFactory, signedDataItemSize } = await streamSignerReadableStream({
|
48
|
+
streamFactory: createUint8ArrayReadableStreamFactory({
|
49
|
+
data: fileStreamFactory(),
|
50
|
+
}),
|
51
|
+
signer: this.signer,
|
52
|
+
dataItemOpts,
|
53
|
+
fileSize,
|
54
|
+
emitter,
|
55
|
+
});
|
56
|
+
this.logger.debug('Successfully signed data item...');
|
57
|
+
return {
|
58
|
+
dataItemStreamFactory: signedDataItemFactory,
|
59
|
+
dataItemSizeFactory: () => signedDataItemSize,
|
60
|
+
};
|
101
61
|
}
|
102
62
|
async generateSignedRequestHeaders() {
|
103
63
|
await this.setPublicKey();
|
@@ -108,3 +68,112 @@ export class TurboWebArweaveSigner extends TurboDataItemAbstractSigner {
|
|
108
68
|
return super.signData(dataToSign);
|
109
69
|
}
|
110
70
|
}
|
71
|
+
export const readableStreamToAsyncIterable = (stream) => ({
|
72
|
+
async *[Symbol.asyncIterator]() {
|
73
|
+
const reader = stream.getReader();
|
74
|
+
try {
|
75
|
+
while (true) {
|
76
|
+
const { done, value } = await reader.read();
|
77
|
+
if (done)
|
78
|
+
break;
|
79
|
+
if (value !== undefined)
|
80
|
+
yield Buffer.from(value);
|
81
|
+
}
|
82
|
+
}
|
83
|
+
finally {
|
84
|
+
reader.releaseLock();
|
85
|
+
}
|
86
|
+
},
|
87
|
+
});
|
88
|
+
export async function streamSignerReadableStream({ streamFactory, signer, dataItemOpts, fileSize, emitter, }) {
|
89
|
+
try {
|
90
|
+
const header = createData('', signer, dataItemOpts);
|
91
|
+
const headerSize = header.getRaw().byteLength;
|
92
|
+
const totalDataItemSizeWithHeader = fileSize + headerSize;
|
93
|
+
const [stream1, stream2] = streamFactory().tee();
|
94
|
+
const reader1 = stream1.getReader();
|
95
|
+
let bytesProcessed = 0;
|
96
|
+
const eventingStream = new ReadableStream({
|
97
|
+
start() {
|
98
|
+
// technically this should be emitted after each DeepHashChunk be we cannot access that stage, so we emit it here instead
|
99
|
+
bytesProcessed = headerSize;
|
100
|
+
emitter?.emit('signing-progress', {
|
101
|
+
processedBytes: bytesProcessed,
|
102
|
+
totalBytes: totalDataItemSizeWithHeader,
|
103
|
+
});
|
104
|
+
},
|
105
|
+
async pull(controller) {
|
106
|
+
const { done, value } = await reader1.read();
|
107
|
+
if (done) {
|
108
|
+
controller.close();
|
109
|
+
return;
|
110
|
+
}
|
111
|
+
bytesProcessed += value.byteLength;
|
112
|
+
controller.enqueue(value);
|
113
|
+
emitter?.emit('signing-progress', {
|
114
|
+
processedBytes: bytesProcessed,
|
115
|
+
totalBytes: totalDataItemSizeWithHeader,
|
116
|
+
});
|
117
|
+
},
|
118
|
+
cancel() {
|
119
|
+
reader1.cancel();
|
120
|
+
},
|
121
|
+
});
|
122
|
+
// create a readable that emits signing events as bytes are pulled through using the first stream from .tee()
|
123
|
+
const asyncIterableReadableStream = readableStreamToAsyncIterable(eventingStream);
|
124
|
+
// provide that ReadableStream with events to deep hash, so as it pulls bytes through events get emitted
|
125
|
+
const parts = [
|
126
|
+
stringToBuffer('dataitem'),
|
127
|
+
stringToBuffer('1'),
|
128
|
+
stringToBuffer(header.signatureType.toString()),
|
129
|
+
Uint8Array.from(header.rawOwner),
|
130
|
+
Uint8Array.from(header.rawTarget),
|
131
|
+
Uint8Array.from(header.rawAnchor),
|
132
|
+
Uint8Array.from(header.rawTags),
|
133
|
+
asyncIterableReadableStream,
|
134
|
+
];
|
135
|
+
const hash = await deepHash(parts);
|
136
|
+
const sigBytes = Buffer.from(await signer.sign(hash));
|
137
|
+
emitter?.emit('signing-success');
|
138
|
+
header.setSignature(sigBytes);
|
139
|
+
const headerBytes = header.getRaw();
|
140
|
+
const signedDataItemFactory = () => {
|
141
|
+
const reader = stream2.getReader();
|
142
|
+
return new ReadableStream({
|
143
|
+
start(controller) {
|
144
|
+
controller.enqueue(Uint8Array.from(headerBytes));
|
145
|
+
bytesProcessed += headerBytes.byteLength;
|
146
|
+
},
|
147
|
+
async pull(controller) {
|
148
|
+
try {
|
149
|
+
const { done, value } = await reader.read();
|
150
|
+
if (done) {
|
151
|
+
controller.close();
|
152
|
+
return;
|
153
|
+
}
|
154
|
+
controller.enqueue(value);
|
155
|
+
}
|
156
|
+
catch (error) {
|
157
|
+
controller.error(error);
|
158
|
+
}
|
159
|
+
},
|
160
|
+
cancel() {
|
161
|
+
reader.cancel();
|
162
|
+
},
|
163
|
+
});
|
164
|
+
};
|
165
|
+
return {
|
166
|
+
signedDataItemSize: totalDataItemSizeWithHeader,
|
167
|
+
signedDataItemFactory,
|
168
|
+
};
|
169
|
+
}
|
170
|
+
catch (error) {
|
171
|
+
emitter?.emit('signing-error', error);
|
172
|
+
throw error;
|
173
|
+
}
|
174
|
+
}
|
175
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
176
|
+
export function isAsyncIterable(data) {
|
177
|
+
return (typeof data[Symbol.asyncIterator] ===
|
178
|
+
'function');
|
179
|
+
}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../src/common/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAe,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAE/C,OAAO,EACL,4BAA4B,EAC5B,6BAA6B,EAC7B,0BAA0B,EAC1B,2BAA2B,EAC3B,2BAA2B,EAC3B,4BAA4B,EAC7B,MAAM,aAAa,CAAC;
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../src/common/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAe,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAE/C,OAAO,EACL,4BAA4B,EAC5B,6BAA6B,EAC7B,0BAA0B,EAC1B,2BAA2B,EAC3B,2BAA2B,EAC3B,4BAA4B,EAC7B,MAAM,aAAa,CAAC;AA4LrB;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,aAAa,GACd,EAAE;IACD,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,cAAc,CAAC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,aAAa,EAAE;QACb,aAAa,EACT,MAAM,4BAA4B,GAClC,MAAM,6BAA6B,GACnC,MAAM,2BAA2B,CAAC;QACtC,UAAU,EACN,MAAM,4BAA4B,GAClC,MAAM,6BAA6B,GACnC,MAAM,2BAA2B,CAAC;QACtC,QAAQ,EACJ,MAAM,4BAA4B,GAClC,MAAM,6BAA6B,GACnC,MAAM,2BAA2B,CAAC;KACvC,CAAC;CACH,GAAG;IAAE,MAAM,EAAE,QAAQ,GAAG,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,IAAI,CAAA;CAAE,CAuB5D;AAED,MAAM,MAAM,uBAAuB,GAAG,4BAA4B,GAChE,6BAA6B,GAC7B,2BAA2B,CAAC;AAC9B,MAAM,MAAM,0BAA0B,GAAG,2BAA2B,GAClE,4BAA4B,GAC5B,0BAA0B,CAAC;AAE7B,qBAAa,iBAAkB,SAAQ,YAAY,CAAC,uBAAuB,CAAC;gBAC9D,EACV,UAAU,EACV,OAAO,EACP,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,gBAAgB,GACjB,GAAE,0BAA+B;CA2DnC;AAED,wBAAgB,4BAA4B,CAAC,EAC3C,IAAI,EACJ,QAAQ,EACR,OAAiC,GAClC,EAAE;IACD,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,cAAc,CAAC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC7B,GAAG;IAAE,MAAM,EAAE,QAAQ,GAAG,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,IAAI,CAAA;CAAE,CAW5D;AAED,wBAAgB,6BAA6B,CAAC,EAC5C,IAAI,EACJ,QAAQ,EACR,OAAiC,GAClC,EAAE;IACD,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,cAAc,CAAC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC7B,GAAG;IAAE,MAAM,EAAE,QAAQ,GAAG,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,IAAI,CAAA;CAAE,CAW5D"}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../../src/common/upload.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGvC,OAAO,EACL,eAAe,EACf,mBAAmB,EAEnB,SAAS,EACT,gBAAgB,EAChB,4CAA4C,EAC5C,wCAAwC,EACxC,oCAAoC,EACpC,mBAAmB,EACnB,gBAAgB,EAChB,WAAW,EACX,wBAAwB,EACxB,0BAA0B,EAC1B,8CAA8C,EAC9C,0CAA0C,EAC1C,kCAAkC,EAClC,2BAA2B,EAC3B,wBAAwB,EACxB,uBAAuB,EACvB,yBAAyB,EACzB,eAAe,
|
1
|
+
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../../src/common/upload.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGvC,OAAO,EACL,eAAe,EACf,mBAAmB,EAEnB,SAAS,EACT,gBAAgB,EAChB,4CAA4C,EAC5C,wCAAwC,EACxC,oCAAoC,EACpC,mBAAmB,EACnB,gBAAgB,EAChB,WAAW,EACX,wBAAwB,EACxB,0BAA0B,EAC1B,8CAA8C,EAC9C,0CAA0C,EAC1C,kCAAkC,EAClC,2BAA2B,EAC3B,wBAAwB,EACxB,uBAAuB,EACvB,yBAAyB,EACzB,eAAe,EAChB,MAAM,aAAa,CAAC;AAKrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAG7C,eAAO,MAAM,qBAAqB;;;;;CAKjC,CAAC;AAEF,eAAO,MAAM,2BAA2B,+BAA+B,CAAC;AACxE,eAAO,MAAM,uBAAuB,8BAA8B,CAAC;AAEnE,qBAAa,iCACX,YAAW,0CAA0C;IAErD,SAAS,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACxC,SAAS,CAAC,MAAM,EAAE,WAAW,CAAC;IAC9B,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC;IAC3B,SAAS,CAAC,WAAW,EAAE,iBAAiB,CAAC;gBAC7B,EACV,GAA6B,EAC7B,MAAmC,EACnC,WAAwC,EACxC,KAAiB,GAClB,EAAE,8CAA8C;IAW3C,oBAAoB,CAAC,EACzB,qBAAqB,EACrB,mBAAmB,EACnB,YAAY,EACZ,MAAM,EACN,MAAW,GACZ,EAAE,0BAA0B,GAC3B,gBAAgB,GAChB,wBAAwB,GAAG,OAAO,CAAC,2BAA2B,CAAC;CA4ClE;AAGD,8BAAsB,mCACpB,SAAQ,iCACR,YAAW,wCAAwC;IAEnD,SAAS,CAAC,MAAM,EAAE,mBAAmB,CAAC;gBAE1B,EACV,GAA6B,EAC7B,WAAW,EACX,MAAM,EACN,MAAM,EACN,KAAK,GACN,EAAE,4CAA4C;IAK/C;;OAEG;IACH,MAAM,CAAC,EACL,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,MAAM,GACP,EAAE,eAAe,GAChB,gBAAgB,GAChB,kCAAkC,GAAG,OAAO,CAAC,2BAA2B,CAAC;IAiCrE,UAAU,CAAC,EACf,iBAAiB,EACjB,eAAe,EACf,MAAM,EACN,YAAY,EACZ,MAAW,GACZ,EAAE,gBAAgB,GACjB,gBAAgB,GAChB,kCAAkC,GAAG,OAAO,CAAC,2BAA2B,CAAC;cAqF3D,gBAAgB,CAAC,EAC/B,KAAK,EACL,SAAS,EACT,YAAY,GACb,EAAE;QACD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACtC,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,GAAG,OAAO,CAAC,eAAe,CAAC;IA6B5B,QAAQ,CAAC,QAAQ,CACf,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAAC,CAAC,IAAI,GAAG,MAAM,CAAC,EAAE,CAAC;IAC7B,QAAQ,CAAC,mBAAmB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM;IACzD,QAAQ,CAAC,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,QAAQ,GAAG,cAAc;IAC7E,QAAQ,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM;IACjD,QAAQ,CAAC,eAAe,CACtB,IAAI,EAAE,MAAM,GAAG,IAAI,EACnB,MAAM,EAAE,uBAAuB,GAC9B,MAAM;IACT,QAAQ,CAAC,oBAAoB,CAC3B,cAAc,EAAE,MAAM,GACrB,QAAQ,GAAG,cAAc;IAE5B,OAAO,CAAC,cAAc;IActB;;;;;;;OAOG;IACG,YAAY,CAChB,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAAC,yBAAyB,CAAC;IAuGxB,YAAY,CAAC,EACxB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,GACjB,EAAE,oCAAoC,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAqCzD,aAAa,CAAC,EACzB,cAAc,GACf,EAAE,wBAAwB,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;CAwB7D"}
|
@@ -13,8 +13,14 @@
|
|
13
13
|
* See the License for the specific language governing permissions and
|
14
14
|
* limitations under the License.
|
15
15
|
*/
|
16
|
+
export declare const DEFAULT_STREAM_CHUNK_SIZE: number;
|
16
17
|
export declare function readableStreamToBuffer({ stream, size, }: {
|
17
18
|
stream: ReadableStream;
|
18
19
|
size: number;
|
19
20
|
}): Promise<Buffer>;
|
21
|
+
export declare function ensureChunkedStream(input: ReadableStream<Uint8Array>, maxChunkSize?: number): ReadableStream<Uint8Array>;
|
22
|
+
export declare function createUint8ArrayReadableStreamFactory({ data, maxChunkSize, }: {
|
23
|
+
data: string | Uint8Array | ArrayBuffer | Buffer | SharedArrayBuffer | Blob | ReadableStream;
|
24
|
+
maxChunkSize?: number;
|
25
|
+
}): () => ReadableStream<Uint8Array>;
|
20
26
|
//# sourceMappingURL=readableStream.d.ts.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"readableStream.d.ts","sourceRoot":"","sources":["../../../src/utils/readableStream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;
|
1
|
+
{"version":3,"file":"readableStream.d.ts","sourceRoot":"","sources":["../../../src/utils/readableStream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,eAAO,MAAM,yBAAyB,QAAmB,CAAC;AAE1D,wBAAsB,sBAAsB,CAAC,EAC3C,MAAM,EACN,IAAI,GACL,EAAE;IACD,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC,MAAM,CAAC,CAelB;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,cAAc,CAAC,UAAU,CAAC,EACjC,YAAY,SAA4B,GACvC,cAAc,CAAC,UAAU,CAAC,CAoC5B;AAED,wBAAgB,qCAAqC,CAAC,EACpD,IAAI,EACJ,YAAwC,GACzC,EAAE;IACD,IAAI,EACA,MAAM,GACN,UAAU,GACV,WAAW,GACX,MAAM,GACN,iBAAiB,GACjB,IAAI,GACJ,cAAc,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,MAAM,cAAc,CAAC,UAAU,CAAC,CA6DnC"}
|