@autonomys/auto-drive 1.1.4 → 1.2.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.
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.getToolsFromAutoDagData = exports.AutoCID = void 0;
46
+ class AutoCID {
47
+ constructor(cid, tools) {
48
+ this.cid = cid;
49
+ this.tools = tools;
50
+ }
51
+ static create(cid) {
52
+ return __awaiter(this, void 0, void 0, function* () {
53
+ const tools = yield exports.getToolsFromAutoDagData;
54
+ return new AutoCID(cid, tools);
55
+ });
56
+ }
57
+ get asString() {
58
+ return this.cid;
59
+ }
60
+ get asCID() {
61
+ return this.tools.stringToCid(this.cid);
62
+ }
63
+ get asBlake3Hash() {
64
+ return Buffer.from(this.tools.blake3HashFromCid(this.asCID));
65
+ }
66
+ }
67
+ exports.AutoCID = AutoCID;
68
+ exports.getToolsFromAutoDagData = Promise.resolve().then(() => __importStar(require('@autonomys/auto-dag-data'))).then(({ stringToCid, blake3HashFromCid }) => {
69
+ return {
70
+ stringToCid,
71
+ blake3HashFromCid,
72
+ };
73
+ });
@@ -1,5 +1,4 @@
1
1
  export * from './async';
2
2
  export * from './misc';
3
- export * from './observable';
4
3
  export * from './types';
5
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,QAAQ,CAAA;AACtB,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,QAAQ,CAAA;AACtB,cAAc,SAAS,CAAA"}
@@ -16,5 +16,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./async"), exports);
18
18
  __exportStar(require("./misc"), exports);
19
- __exportStar(require("./observable"), exports);
20
19
  __exportStar(require("./types"), exports);
@@ -1,7 +1,2 @@
1
- import * as rxjs from 'rxjs';
2
- export declare class PromisedObservable<T> extends rxjs.Observable<T> {
3
- constructor(subscribe?: (this: rxjs.Observable<T>, subscriber: rxjs.Subscriber<T>) => void);
4
- get promise(): Promise<T>;
5
- }
6
- export declare const firstValueFrom: typeof rxjs.firstValueFrom, lastValueFrom: typeof rxjs.lastValueFrom;
1
+ export {};
7
2
  //# sourceMappingURL=observable.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"observable.d.ts","sourceRoot":"","sources":["../../src/utils/observable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAQ5B,qBAAa,kBAAkB,CAAC,CAAC,CAAE,SAAQ,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;gBAC/C,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI;IAI1F,IAAI,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,CAExB;CACF;AAED,eAAO,MAAQ,cAAc,8BAAE,aAAa,2BAAS,CAAA"}
1
+ {"version":3,"file":"observable.d.ts","sourceRoot":"","sources":["../../src/utils/observable.ts"],"names":[],"mappings":""}
@@ -1,52 +1,2 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.lastValueFrom = exports.firstValueFrom = exports.PromisedObservable = void 0;
37
- const rxjs = __importStar(require("rxjs"));
38
- const asyncCallback = (callback) => {
39
- return (t) => {
40
- callback(t);
41
- };
42
- };
43
- class PromisedObservable extends rxjs.Observable {
44
- constructor(subscribe) {
45
- super(subscribe && asyncCallback(subscribe));
46
- }
47
- get promise() {
48
- return (0, exports.lastValueFrom)(this);
49
- }
50
- }
51
- exports.PromisedObservable = PromisedObservable;
52
- exports.firstValueFrom = rxjs.firstValueFrom, exports.lastValueFrom = rxjs.lastValueFrom;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@autonomys/auto-drive",
3
3
  "packageManager": "yarn@4.2.2",
4
- "version": "1.1.4",
4
+ "version": "1.2.1",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -42,13 +42,12 @@
42
42
  "typescript": "^5.6.3"
43
43
  },
44
44
  "dependencies": {
45
- "@autonomys/auto-dag-data": "^1.1.4",
45
+ "@autonomys/auto-dag-data": "^1.2.1",
46
46
  "jszip": "^3.10.1",
47
47
  "mime-types": "^2.1.35",
48
48
  "process": "^0.11.10",
49
- "rxjs": "^7.8.1",
50
49
  "stream": "^0.0.3",
51
50
  "zod": "^3.23.8"
52
51
  },
53
- "gitHead": "8e804316b27de8568cf5a71d3ec2500f6e262d3c"
52
+ "gitHead": "72285a024adcc7681ec59fabd1f061a6e40aeb09"
54
53
  }
@@ -2,6 +2,7 @@ import { ArgsWithoutPagination, ArgsWithPagination } from '../../utils/types'
2
2
  import { AutoDriveApi } from '../connection'
3
3
  import { PaginatedResult } from '../models/common'
4
4
  import { ObjectInformation, ObjectSummary, Scope } from '../models/objects'
5
+ import { UserInfo } from '../models/user'
5
6
 
6
7
  /**
7
8
  * Retrieves the root objects based on the specified scope.
@@ -193,3 +194,21 @@ export const getObjectMetadata = async (
193
194
 
194
195
  return response.json()
195
196
  }
197
+
198
+ /**
199
+ * Get upload and download limits of the user
200
+ *
201
+ * @param {AutoDriveApi} api - The API instance used to send requests.
202
+ * @returns {Promise<UserInfo>} - A promise that resolves to the user info.
203
+ * @throws {Error} - Throws an error if the request fails.
204
+ */
205
+ export const getMe = async (api: AutoDriveApi): Promise<UserInfo> => {
206
+ const response = await api.sendRequest('@me', {
207
+ method: 'GET',
208
+ })
209
+ if (!response.ok) {
210
+ throw new Error(`Failed to get limits: ${response.statusText}`)
211
+ }
212
+
213
+ return response.json()
214
+ }
@@ -0,0 +1,31 @@
1
+ export type SubscriptionGranularity = 'monthly'
2
+
3
+ export type SubscriptionInfo = {
4
+ id: string
5
+ organizationId: string
6
+ uploadLimit: number
7
+ downloadLimit: number
8
+ granularity: SubscriptionGranularity
9
+ pendingUploadCredits: number
10
+ pendingDownloadCredits: number
11
+ }
12
+
13
+ export enum UserRole {
14
+ User = 'User',
15
+ Admin = 'Admin',
16
+ }
17
+
18
+ export type User = {
19
+ oauthProvider: string
20
+ oauthUserId: string
21
+ role: UserRole
22
+ downloadCredits: number
23
+ uploadCredits: number
24
+ publicId: string
25
+ onboarded: true
26
+ }
27
+
28
+ export type UserInfo = {
29
+ user: User
30
+ subscription: SubscriptionInfo
31
+ }
@@ -1,16 +1,16 @@
1
1
  import mime from 'mime-types'
2
- import { asyncByChunk, asyncFromStream, fileToIterable } from '../utils/async'
2
+ import { asyncByChunk, asyncFromStream, bufferToIterable, fileToIterable } from '../utils/async'
3
3
  import { progressToPercentage } from '../utils/misc'
4
- import { PromisedObservable } from '../utils/observable'
5
4
  import { apiCalls } from './calls/index'
6
5
  import { AutoDriveApi } from './connection'
7
6
  import { GenericFile, GenericFileWithinFolder } from './models/file'
8
7
  import { constructFromInput, constructZipBlobFromTreeAndPaths } from './models/folderTree'
9
- import { UploadChunksStatus, UploadFileStatus, UploadFolderStatus } from './models/uploads'
8
+ import { SubscriptionInfo } from './models/user'
10
9
 
11
10
  export type UploadFileOptions = {
12
11
  password?: string
13
12
  compression?: boolean
13
+ onProgress?: (progress: number) => void
14
14
  }
15
15
 
16
16
  const UPLOAD_FILE_CHUNK_SIZE = 1024 * 1024
@@ -20,17 +20,23 @@ const uploadFileChunks = (
20
20
  fileUploadId: string,
21
21
  asyncIterable: AsyncIterable<Buffer>,
22
22
  uploadChunkSize: number = UPLOAD_FILE_CHUNK_SIZE,
23
- ): PromisedObservable<UploadChunksStatus> => {
24
- return new PromisedObservable<UploadChunksStatus>(async (subscriber) => {
25
- let index = 0
26
- let uploadBytes = 0
27
- for await (const chunk of asyncByChunk(asyncIterable, uploadChunkSize)) {
28
- await apiCalls.uploadFileChunk(api, { uploadId: fileUploadId, chunk, index })
29
- uploadBytes += chunk.length
30
- subscriber.next({ uploadBytes })
31
- index++
23
+ onProgress?: (uploadedBytes: number) => void,
24
+ ): Promise<void> => {
25
+ return new Promise(async (resolve, reject) => {
26
+ try {
27
+ let index = 0
28
+ let uploadBytes = 0
29
+ for await (const chunk of asyncByChunk(asyncIterable, uploadChunkSize)) {
30
+ await apiCalls.uploadFileChunk(api, { uploadId: fileUploadId, chunk, index })
31
+ uploadBytes += chunk.length
32
+ onProgress?.(uploadBytes)
33
+ index++
34
+ }
35
+
36
+ resolve()
37
+ } catch (e) {
38
+ reject(e)
32
39
  }
33
- subscriber.complete()
34
40
  })
35
41
  }
36
42
 
@@ -56,11 +62,12 @@ export const uploadFileFromInput = (
56
62
  file: File,
57
63
  options: UploadFileOptions = {},
58
64
  uploadChunkSize?: number,
59
- ): PromisedObservable<UploadFileStatus> => {
65
+ ): Promise<string> => {
60
66
  const { password = undefined, compression = true } = options
61
- return new PromisedObservable<UploadFileStatus>(async (subscriber) => {
62
- const { stringToCid, compressFile, CompressionAlgorithm, encryptFile, EncryptionAlgorithm } =
63
- await import('@autonomys/auto-dag-data')
67
+ return new Promise(async (resolve, reject) => {
68
+ const { compressFile, CompressionAlgorithm, encryptFile, EncryptionAlgorithm } = await import(
69
+ '@autonomys/auto-dag-data'
70
+ )
64
71
  let asyncIterable: AsyncIterable<Buffer> = fileToIterable(file)
65
72
 
66
73
  if (compression) {
@@ -95,14 +102,13 @@ export const uploadFileFromInput = (
95
102
  uploadOptions,
96
103
  })
97
104
 
98
- await uploadFileChunks(api, fileUpload.id, asyncIterable, uploadChunkSize).forEach((e) =>
99
- subscriber.next({ type: 'file', progress: progressToPercentage(e.uploadBytes, file.size) }),
100
- )
105
+ await uploadFileChunks(api, fileUpload.id, asyncIterable, uploadChunkSize, (bytes) => {
106
+ options.onProgress?.(progressToPercentage(bytes, file.size))
107
+ })
101
108
 
102
109
  const result = await apiCalls.completeUpload(api, { uploadId: fileUpload.id })
103
110
 
104
- subscriber.next({ type: 'file', progress: 100, cid: result.cid })
105
- subscriber.complete()
111
+ resolve(result.cid)
106
112
  })
107
113
  }
108
114
 
@@ -120,63 +126,101 @@ export const uploadFileFromInput = (
120
126
  * @param {string} [options.password] - The password for encryption (optional).
121
127
  * @param {boolean} [options.compression=true] - Whether to compress the file (optional).
122
128
  * @param {number} [uploadChunkSize] - The size of each chunk to upload (optional).
123
- * @returns {PromisedObservable<UploadFileStatus>} - An observable that emits the upload status.
129
+ * @returns {Promise<string>} - The CID of the uploaded file.
124
130
  * @throws {Error} - Throws an error if the upload fails at any stage.
125
131
  */
126
- export const uploadFile = (
132
+ export const uploadFile = async (
127
133
  api: AutoDriveApi,
128
134
  file: GenericFile,
129
135
  options: UploadFileOptions = {},
130
136
  uploadChunkSize?: number,
131
- ): PromisedObservable<UploadFileStatus> => {
137
+ ): Promise<string> => {
132
138
  const { password = undefined, compression = true } = options
133
139
 
134
- return new PromisedObservable<UploadFileStatus>(async (subscriber) => {
135
- const { stringToCid, compressFile, CompressionAlgorithm, encryptFile, EncryptionAlgorithm } =
136
- await import('@autonomys/auto-dag-data')
137
- let asyncIterable: AsyncIterable<Buffer> = file.read()
138
-
139
- if (compression) {
140
- asyncIterable = compressFile(asyncIterable, {
141
- level: 9,
142
- algorithm: CompressionAlgorithm.ZLIB,
143
- })
144
- }
145
-
146
- if (password) {
147
- asyncIterable = encryptFile(asyncIterable, password, {
148
- algorithm: EncryptionAlgorithm.AES_256_GCM,
149
- })
150
- }
140
+ const { compressFile, CompressionAlgorithm, encryptFile, EncryptionAlgorithm } = await import(
141
+ '@autonomys/auto-dag-data'
142
+ )
143
+ let asyncIterable: AsyncIterable<Buffer> = file.read()
151
144
 
152
- const uploadOptions = {
153
- compression: compression
154
- ? {
155
- level: 9,
156
- algorithm: CompressionAlgorithm.ZLIB,
157
- }
158
- : undefined,
159
- encryption: password
160
- ? {
161
- algorithm: EncryptionAlgorithm.AES_256_GCM,
162
- }
163
- : undefined,
164
- }
165
- const fileUpload = await apiCalls.createFileUpload(api, {
166
- mimeType: mime.lookup(file.name) || undefined,
167
- filename: file.name,
168
- uploadOptions,
145
+ if (compression) {
146
+ asyncIterable = compressFile(asyncIterable, {
147
+ level: 9,
148
+ algorithm: CompressionAlgorithm.ZLIB,
169
149
  })
150
+ }
170
151
 
171
- await uploadFileChunks(api, fileUpload.id, asyncIterable, uploadChunkSize).forEach((e) =>
172
- subscriber.next({ type: 'file', progress: progressToPercentage(e.uploadBytes, file.size) }),
173
- )
152
+ if (password) {
153
+ asyncIterable = encryptFile(asyncIterable, password, {
154
+ algorithm: EncryptionAlgorithm.AES_256_GCM,
155
+ })
156
+ }
174
157
 
175
- const result = await apiCalls.completeUpload(api, { uploadId: fileUpload.id })
158
+ const uploadOptions = {
159
+ compression: compression
160
+ ? {
161
+ level: 9,
162
+ algorithm: CompressionAlgorithm.ZLIB,
163
+ }
164
+ : undefined,
165
+ encryption: password
166
+ ? {
167
+ algorithm: EncryptionAlgorithm.AES_256_GCM,
168
+ }
169
+ : undefined,
170
+ }
171
+ const fileUpload = await apiCalls.createFileUpload(api, {
172
+ mimeType: mime.lookup(file.name) || undefined,
173
+ filename: file.name,
174
+ uploadOptions,
175
+ })
176
176
 
177
- subscriber.next({ type: 'file', progress: 100, cid: result.cid })
178
- subscriber.complete()
177
+ await uploadFileChunks(api, fileUpload.id, asyncIterable, uploadChunkSize, (bytes) => {
178
+ options.onProgress?.(progressToPercentage(bytes, file.size))
179
179
  })
180
+
181
+ const result = await apiCalls.completeUpload(api, { uploadId: fileUpload.id })
182
+
183
+ return result.cid
184
+ }
185
+
186
+ /**
187
+ * Uploads an object as a JSON file to the server.
188
+ *
189
+ * This function serializes the provided object to a JSON string,
190
+ * and then uploads the JSON string as a file to the server.
191
+ *
192
+ * @param {AutoDriveApi} api - The API instance used to send requests.
193
+ * @param {File | GenericFile} file - The file to be uploaded, which can be a File or a GenericFile.
194
+ * @param {UploadFileOptions} options - Options for the upload process.
195
+ * @param {string} [options.password] - The password for encryption (optional).
196
+ * @param {boolean} [options.compression=true] - Whether to compress the file (optional).
197
+ * @param {number} [uploadChunkSize] - The size of each chunk to upload (optional).
198
+ * @returns {Promise<string>} - The CID of the uploaded file.
199
+ * @throws {Error} - Throws an error if the upload fails at any stage.
200
+ */
201
+ export const uploadObjectAsJSON = async (
202
+ api: AutoDriveApi,
203
+ object: unknown,
204
+ name?: string | undefined,
205
+ options: UploadFileOptions = {},
206
+ uploadChunkSize?: number,
207
+ ): Promise<string> => {
208
+ try {
209
+ const json = Buffer.from(JSON.stringify(object))
210
+ return uploadFile(
211
+ api,
212
+ {
213
+ read: () => bufferToIterable(json),
214
+ name: name || 'object.json',
215
+ mimeType: 'application/json',
216
+ size: json.length,
217
+ },
218
+ options,
219
+ uploadChunkSize,
220
+ )
221
+ } catch (e) {
222
+ throw new Error('Failed to serialize object to JSON')
223
+ }
180
224
  }
181
225
 
182
226
  /**
@@ -199,8 +243,12 @@ export const uploadFile = (
199
243
  export const uploadFolderFromInput = async (
200
244
  api: AutoDriveApi,
201
245
  fileList: FileList | File[],
202
- { uploadChunkSize, password }: { uploadChunkSize?: number; password?: string } = {},
203
- ): Promise<PromisedObservable<UploadFileStatus | UploadFolderStatus>> => {
246
+ {
247
+ uploadChunkSize,
248
+ password,
249
+ onProgress,
250
+ }: { uploadChunkSize?: number; password?: string; onProgress?: (progress: number) => void } = {},
251
+ ): Promise<string> => {
204
252
  const files = fileList instanceof FileList ? Array.from(fileList) : fileList
205
253
  const fileTree = constructFromInput(files)
206
254
 
@@ -223,45 +271,42 @@ export const uploadFolderFromInput = async (
223
271
  {
224
272
  password,
225
273
  compression: true,
274
+ onProgress,
226
275
  },
227
276
  )
228
277
  }
229
278
 
230
- return new PromisedObservable<UploadFolderStatus>(async (subscriber) => {
231
- // Otherwise, we upload the files as a folder w/o compression or encryption
232
- const folderUpload = await apiCalls.createFolderUpload(api, {
233
- fileTree,
234
- })
279
+ // Otherwise, we upload the files as a folder w/o compression or encryption
280
+ const folderUpload = await apiCalls.createFolderUpload(api, {
281
+ fileTree,
282
+ })
235
283
 
236
- let currentBytesUploaded = 0
237
- const totalSize = files.reduce((acc, file) => acc + file.size, 0)
238
- for (const file of files) {
239
- await uploadFileWithinFolderUpload(
240
- api,
241
- folderUpload.id,
242
- {
243
- read: () => fileToIterable(file),
244
- name: file.name,
245
- mimeType: mime.lookup(file.name) || undefined,
246
- size: file.size,
247
- path: file.webkitRelativePath,
284
+ let currentBytesUploaded = 0
285
+ const totalSize = files.reduce((acc, file) => acc + file.size, 0)
286
+ for (const file of files) {
287
+ await uploadFileWithinFolderUpload(
288
+ api,
289
+ folderUpload.id,
290
+ {
291
+ read: () => fileToIterable(file),
292
+ name: file.name,
293
+ mimeType: mime.lookup(file.name) || undefined,
294
+ size: file.size,
295
+ path: file.webkitRelativePath,
296
+ },
297
+ uploadChunkSize,
298
+ {
299
+ onProgress: (progress) => {
300
+ onProgress?.(progressToPercentage(currentBytesUploaded + progress, totalSize))
248
301
  },
249
- uploadChunkSize,
250
- ).forEach((e) => {
251
- subscriber.next({
252
- type: 'folder',
253
- progress: progressToPercentage(currentBytesUploaded + e.uploadBytes, totalSize),
254
- })
255
- })
256
-
257
- currentBytesUploaded += file.size
258
- }
302
+ },
303
+ )
304
+ currentBytesUploaded += file.size
305
+ }
259
306
 
260
- const result = await apiCalls.completeUpload(api, { uploadId: folderUpload.id })
307
+ const result = await apiCalls.completeUpload(api, { uploadId: folderUpload.id })
261
308
 
262
- subscriber.next({ type: 'folder', progress: 100, cid: result.cid })
263
- subscriber.complete()
264
- })
309
+ return result.cid
265
310
  }
266
311
 
267
312
  /**
@@ -273,29 +318,26 @@ export const uploadFolderFromInput = async (
273
318
  *
274
319
  * @returns {Promise<void>} A promise that resolves when the file upload is complete.
275
320
  */
276
- export const uploadFileWithinFolderUpload = (
321
+ export const uploadFileWithinFolderUpload = async (
277
322
  api: AutoDriveApi,
278
323
  uploadId: string,
279
324
  file: GenericFileWithinFolder,
280
325
  uploadChunkSize?: number,
281
- ): PromisedObservable<UploadChunksStatus> => {
282
- return new PromisedObservable<UploadChunksStatus>(async (subscriber) => {
283
- const fileUpload = await apiCalls.createFileUploadWithinFolderUpload(api, {
284
- uploadId,
285
- name: file.name,
286
- mimeType: file.mimeType,
287
- relativeId: file.path,
288
- uploadOptions: {},
289
- })
326
+ options: Pick<UploadFileOptions, 'onProgress'> = {},
327
+ ): Promise<string> => {
328
+ const fileUpload = await apiCalls.createFileUploadWithinFolderUpload(api, {
329
+ uploadId,
330
+ name: file.name,
331
+ mimeType: file.mimeType,
332
+ relativeId: file.path,
333
+ uploadOptions: {},
334
+ })
290
335
 
291
- await uploadFileChunks(api, fileUpload.id, file.read(), uploadChunkSize).forEach((e) =>
292
- subscriber.next({ uploadBytes: e.uploadBytes }),
293
- )
336
+ await uploadFileChunks(api, fileUpload.id, file.read(), uploadChunkSize, options.onProgress)
294
337
 
295
- await apiCalls.completeUpload(api, { uploadId: fileUpload.id })
338
+ const result = await apiCalls.completeUpload(api, { uploadId: fileUpload.id })
296
339
 
297
- subscriber.complete()
298
- })
340
+ return result.cid
299
341
  }
300
342
 
301
343
  /**
@@ -334,3 +376,19 @@ export const downloadFile = async (
334
376
 
335
377
  return iterable
336
378
  }
379
+
380
+ export const getPendingCredits = async (
381
+ api: AutoDriveApi,
382
+ ): Promise<{ upload: number; download: number }> => {
383
+ const me = await apiCalls.getMe(api)
384
+ return {
385
+ upload: me.subscription.pendingUploadCredits,
386
+ download: me.subscription.pendingDownloadCredits,
387
+ }
388
+ }
389
+
390
+ export const getSubscriptionInfo = async (api: AutoDriveApi): Promise<SubscriptionInfo> => {
391
+ const me = await apiCalls.getMe(api)
392
+
393
+ return me.subscription
394
+ }