@lumeweb/pinner 0.1.12 → 0.1.14
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/dist/esm/adapters/pinata/index.d.ts +1 -1
- package/dist/esm/adapters/pinata/shared/index.d.ts +1 -1
- package/dist/esm/adapters/pinata/shared/types.d.ts +41 -1
- package/dist/esm/adapters/pinata/v2/adapter-interface.d.ts +4 -1
- package/dist/esm/adapters/pinata/v2/index.d.ts +1 -1
- package/dist/esm/adapters/pinata/v2/types.d.ts +58 -1
- package/dist/esm/api/client.d.ts +21 -0
- package/dist/esm/api/client.js +54 -0
- package/dist/esm/api/client.js.map +1 -0
- package/dist/esm/api/generated/content/content.msw.js +30 -37
- package/dist/esm/api/generated/content/content.msw.js.map +1 -1
- package/dist/esm/api/generated/dns/dns.msw.js +30 -44
- package/dist/esm/api/generated/dns/dns.msw.js.map +1 -1
- package/dist/esm/api/generated/files/files.msw.js +30 -33
- package/dist/esm/api/generated/files/files.msw.js.map +1 -1
- package/dist/esm/api/generated/gateway/gateway.msw.js +30 -32
- package/dist/esm/api/generated/gateway/gateway.msw.js.map +1 -1
- package/dist/esm/api/generated/internal/internal.msw.js +30 -31
- package/dist/esm/api/generated/internal/internal.msw.js.map +1 -1
- package/dist/esm/api/generated/ipns/ipns.msw.js +30 -37
- package/dist/esm/api/generated/ipns/ipns.msw.js.map +1 -1
- package/dist/esm/api/generated/pinning/pinning.msw.js +30 -35
- package/dist/esm/api/generated/pinning/pinning.msw.js.map +1 -1
- package/dist/esm/api/generated/schemas/blockMetaResponse.d.ts +29 -29
- package/dist/esm/api/generated/schemas/component.d.ts +29 -29
- package/dist/esm/api/generated/schemas/errorResponse.d.ts +29 -29
- package/dist/esm/api/generated/schemas/fileManagerItem.d.ts +29 -29
- package/dist/esm/api/generated/schemas/gatewayWebsiteResponse.d.ts +29 -29
- package/dist/esm/api/generated/schemas/gatewayWebsiteStatusResponse.d.ts +29 -29
- package/dist/esm/api/generated/schemas/getBlockMetaBatchRequest.d.ts +42 -0
- package/dist/esm/api/generated/schemas/iPNSKeyListResponse.d.ts +29 -29
- package/dist/esm/api/generated/schemas/iPNSKeyRequest.d.ts +29 -29
- package/dist/esm/api/generated/schemas/iPNSKeyResponse.d.ts +29 -29
- package/dist/esm/api/generated/schemas/iPNSPublishRequest.d.ts +29 -29
- package/dist/esm/api/generated/schemas/iPNSPublishResponse.d.ts +29 -29
- package/dist/esm/api/generated/schemas/iPNSRepublishResponse.d.ts +29 -29
- package/dist/esm/api/generated/schemas/iPNSResolveResponse.d.ts +29 -29
- package/dist/esm/api/generated/schemas/infoResponse.d.ts +29 -29
- package/dist/esm/api/generated/schemas/pinRequestMeta.d.ts +29 -29
- package/dist/esm/api/generated/schemas/pinStatusResponseInfo.d.ts +29 -29
- package/dist/esm/api/generated/schemas/postApiUploadBody.d.ts +42 -0
- package/dist/esm/api/generated/schemas/postUploadResponse.d.ts +29 -29
- package/dist/esm/api/generated/schemas/recordResponse.d.ts +29 -29
- package/dist/esm/api/generated/schemas/recordResult.d.ts +29 -29
- package/dist/esm/api/generated/schemas/sSLStatusInfo.d.ts +29 -29
- package/dist/esm/api/generated/schemas/sSLStatusUpdateRequest.d.ts +44 -0
- package/dist/esm/api/generated/schemas/uploadResultResponse.d.ts +29 -29
- package/dist/esm/api/generated/schemas/validationResponse.d.ts +29 -29
- package/dist/esm/api/generated/schemas/websiteConfigResponse.d.ts +29 -29
- package/dist/esm/api/generated/schemas/websiteRequest.d.ts +29 -29
- package/dist/esm/api/generated/schemas/websiteUpdateRequest.d.ts +29 -29
- package/dist/esm/api/generated/schemas/websiteValidateResponse.d.ts +29 -29
- package/dist/esm/api/generated/schemas/zoneListResponse.d.ts +29 -29
- package/dist/esm/api/generated/schemas/zoneResponse.d.ts +29 -29
- package/dist/esm/api/generated/tus/tus.msw.js +30 -34
- package/dist/esm/api/generated/tus/tus.msw.js.map +1 -1
- package/dist/esm/api/generated/websites/websites.msw.js +30 -38
- package/dist/esm/api/generated/websites/websites.msw.js.map +1 -1
- package/dist/esm/api/ipns.d.ts +47 -6
- package/dist/esm/api/ipns.js +46 -39
- package/dist/esm/api/ipns.js.map +1 -1
- package/dist/esm/api/websites.d.ts +57 -6
- package/dist/esm/api/websites.js +57 -41
- package/dist/esm/api/websites.js.map +1 -1
- package/dist/esm/auth/manager.d.ts +29 -0
- package/dist/esm/auth/manager.js +30 -0
- package/dist/esm/auth/manager.js.map +1 -0
- package/dist/esm/blockstore/index.d.ts +1 -1
- package/dist/esm/blockstore/unstorage-base.d.ts +6 -1
- package/dist/esm/blockstore/unstorage-base.js +9 -6
- package/dist/esm/blockstore/unstorage-base.js.map +1 -1
- package/dist/esm/config.d.ts +13 -1
- package/dist/esm/index.d.ts +31 -6
- package/dist/esm/pin/client.js +7 -7
- package/dist/esm/pin/client.js.map +1 -1
- package/dist/esm/pinner.d.ts +22 -0
- package/dist/esm/pinner.js +28 -4
- package/dist/esm/pinner.js.map +1 -1
- package/dist/esm/types/pin.d.ts +42 -0
- package/dist/esm/upload/base-upload.js +3 -1
- package/dist/esm/upload/base-upload.js.map +1 -1
- package/dist/esm/upload/builder.d.ts +1 -1
- package/dist/esm/upload/car.d.ts +18 -0
- package/dist/esm/upload/car.js.map +1 -1
- package/dist/esm/upload/manager.d.ts +32 -1
- package/dist/esm/upload/manager.js +36 -4
- package/dist/esm/upload/manager.js.map +1 -1
- package/dist/esm/upload/normalize.js.map +1 -1
- package/dist/esm/upload/tus-upload.js +3 -3
- package/dist/esm/upload/tus-upload.js.map +1 -1
- package/dist/esm/upload/xhr-upload.js +3 -1
- package/dist/esm/upload/xhr-upload.js.map +1 -1
- package/package.json +19 -19
package/dist/esm/pinner.d.ts
CHANGED
|
@@ -14,6 +14,11 @@ declare class Pinner {
|
|
|
14
14
|
private _ipns;
|
|
15
15
|
private _websites;
|
|
16
16
|
private _upload?;
|
|
17
|
+
private readonly auth;
|
|
18
|
+
/**
|
|
19
|
+
* Create a new Pinner SDK instance.
|
|
20
|
+
* @param config SDK configuration object
|
|
21
|
+
*/
|
|
17
22
|
constructor(config: PinnerConfig);
|
|
18
23
|
/**
|
|
19
24
|
* Access the remote pins interface.
|
|
@@ -37,6 +42,8 @@ declare class Pinner {
|
|
|
37
42
|
/**
|
|
38
43
|
* Upload a file and wait for completion.
|
|
39
44
|
* Convenience method for simple use cases where controls aren't needed.
|
|
45
|
+
* @param file The file to upload
|
|
46
|
+
* @param options Upload configuration
|
|
40
47
|
*/
|
|
41
48
|
uploadAndWait(file: File, options?: UploadOptions): Promise<UploadResult>;
|
|
42
49
|
/**
|
|
@@ -48,39 +55,54 @@ declare class Pinner {
|
|
|
48
55
|
waitForOperation(input: number | UploadResult, options?: OperationPollingOptions): Promise<UploadResult>;
|
|
49
56
|
/**
|
|
50
57
|
* Upload a directory to IPFS.
|
|
58
|
+
* @param files Array of files to upload as a directory
|
|
59
|
+
* @param options Upload configuration
|
|
51
60
|
*/
|
|
52
61
|
uploadDirectory(files: File[], options?: UploadOptions): Promise<UploadOperation>;
|
|
53
62
|
/**
|
|
54
63
|
* Upload a CAR file without preprocessing.
|
|
55
64
|
* This is useful for passthrough of pre-generated CAR files.
|
|
65
|
+
* @param file CAR file or stream to upload
|
|
66
|
+
* @param options Upload configuration
|
|
56
67
|
*/
|
|
57
68
|
uploadCar(file: File | ReadableStream<Uint8Array>, options?: UploadOptions): Promise<UploadOperation>;
|
|
58
69
|
/**
|
|
59
70
|
* Pin existing content by CID.
|
|
71
|
+
* @param cid CID of content to pin (string or CID object)
|
|
72
|
+
* @param options Remote add options
|
|
60
73
|
*/
|
|
61
74
|
pinByHash(cid: string | CID, options?: RemoteAddOptions): Promise<AsyncGenerator<CID, void, undefined>>;
|
|
62
75
|
/**
|
|
63
76
|
* List pinned content.
|
|
77
|
+
* @param options List filtering options
|
|
64
78
|
*/
|
|
65
79
|
listPins(options?: RemoteLsOptions): Promise<RemotePin[]>;
|
|
66
80
|
/**
|
|
67
81
|
* Get pin status.
|
|
82
|
+
* @param cid CID of the pinned content to check
|
|
68
83
|
*/
|
|
69
84
|
getPinStatus(cid: string | CID): Promise<RemotePin>;
|
|
70
85
|
/**
|
|
71
86
|
* Check if content is pinned.
|
|
87
|
+
* @param cid CID to check
|
|
72
88
|
*/
|
|
73
89
|
isPinned(cid: string | CID): Promise<boolean>;
|
|
74
90
|
/**
|
|
75
91
|
* Update pin metadata.
|
|
92
|
+
* @param cid CID of the pin
|
|
93
|
+
* @param metadata Key-value metadata to set
|
|
76
94
|
*/
|
|
77
95
|
setPinMetadata(cid: string | CID, metadata: Record<string, string> | undefined): Promise<void>;
|
|
78
96
|
/**
|
|
79
97
|
* Remove a pin. The block may be deleted when garbage collection is run.
|
|
98
|
+
* @param cid CID to unpin
|
|
99
|
+
* @param options Abort options
|
|
80
100
|
*/
|
|
81
101
|
unpin(cid: string | CID, options?: AbortOptions): Promise<void>;
|
|
82
102
|
/**
|
|
83
103
|
* Remove a pin by request ID. The block may be deleted when garbage collection is run.
|
|
104
|
+
* @param requestId The request ID to remove
|
|
105
|
+
* @param options Abort options
|
|
84
106
|
*/
|
|
85
107
|
unpinByRequestId(requestId: string, options?: AbortOptions): Promise<void>;
|
|
86
108
|
/**
|
package/dist/esm/pinner.js
CHANGED
|
@@ -5,6 +5,7 @@ import { PinClient } from "./pin/client.js";
|
|
|
5
5
|
import "./pin/index.js";
|
|
6
6
|
import { IpnsClient } from "./api/ipns.js";
|
|
7
7
|
import { WebsitesClient } from "./api/websites.js";
|
|
8
|
+
import { JwtAuthManager } from "./auth/manager.js";
|
|
8
9
|
import { CID } from "multiformats/cid";
|
|
9
10
|
|
|
10
11
|
//#region src/pinner.ts
|
|
@@ -14,11 +15,17 @@ var Pinner = class {
|
|
|
14
15
|
_ipns;
|
|
15
16
|
_websites;
|
|
16
17
|
_upload;
|
|
18
|
+
auth;
|
|
19
|
+
/**
|
|
20
|
+
* Create a new Pinner SDK instance.
|
|
21
|
+
* @param config SDK configuration object
|
|
22
|
+
*/
|
|
17
23
|
constructor(config) {
|
|
18
|
-
this.
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
21
|
-
this.
|
|
24
|
+
this.auth = new JwtAuthManager(config.jwt);
|
|
25
|
+
this.uploadManager = new UploadManager(config, this.auth);
|
|
26
|
+
this._pins = new PinClient(config, this.auth);
|
|
27
|
+
this._ipns = new IpnsClient(config, this.auth);
|
|
28
|
+
this._websites = new WebsitesClient(config, this.auth);
|
|
22
29
|
}
|
|
23
30
|
/**
|
|
24
31
|
* Access the remote pins interface.
|
|
@@ -60,6 +67,8 @@ var Pinner = class {
|
|
|
60
67
|
/**
|
|
61
68
|
* Upload a file and wait for completion.
|
|
62
69
|
* Convenience method for simple use cases where controls aren't needed.
|
|
70
|
+
* @param file The file to upload
|
|
71
|
+
* @param options Upload configuration
|
|
63
72
|
*/
|
|
64
73
|
async uploadAndWait(file, options) {
|
|
65
74
|
return (await this.upload(file, options)).result;
|
|
@@ -75,6 +84,8 @@ var Pinner = class {
|
|
|
75
84
|
}
|
|
76
85
|
/**
|
|
77
86
|
* Upload a directory to IPFS.
|
|
87
|
+
* @param files Array of files to upload as a directory
|
|
88
|
+
* @param options Upload configuration
|
|
78
89
|
*/
|
|
79
90
|
async uploadDirectory(files, options) {
|
|
80
91
|
return this.uploadManager.uploadDirectory(files, options);
|
|
@@ -82,12 +93,16 @@ var Pinner = class {
|
|
|
82
93
|
/**
|
|
83
94
|
* Upload a CAR file without preprocessing.
|
|
84
95
|
* This is useful for passthrough of pre-generated CAR files.
|
|
96
|
+
* @param file CAR file or stream to upload
|
|
97
|
+
* @param options Upload configuration
|
|
85
98
|
*/
|
|
86
99
|
async uploadCar(file, options) {
|
|
87
100
|
return this.uploadManager.uploadCar(file, options);
|
|
88
101
|
}
|
|
89
102
|
/**
|
|
90
103
|
* Pin existing content by CID.
|
|
104
|
+
* @param cid CID of content to pin (string or CID object)
|
|
105
|
+
* @param options Remote add options
|
|
91
106
|
*/
|
|
92
107
|
async pinByHash(cid, options) {
|
|
93
108
|
const cidObj = typeof cid === "string" ? CID.parse(cid) : cid;
|
|
@@ -95,6 +110,7 @@ var Pinner = class {
|
|
|
95
110
|
}
|
|
96
111
|
/**
|
|
97
112
|
* List pinned content.
|
|
113
|
+
* @param options List filtering options
|
|
98
114
|
*/
|
|
99
115
|
async listPins(options) {
|
|
100
116
|
const pins = [];
|
|
@@ -103,6 +119,7 @@ var Pinner = class {
|
|
|
103
119
|
}
|
|
104
120
|
/**
|
|
105
121
|
* Get pin status.
|
|
122
|
+
* @param cid CID of the pinned content to check
|
|
106
123
|
*/
|
|
107
124
|
async getPinStatus(cid) {
|
|
108
125
|
const cidObj = typeof cid === "string" ? CID.parse(cid) : cid;
|
|
@@ -110,6 +127,7 @@ var Pinner = class {
|
|
|
110
127
|
}
|
|
111
128
|
/**
|
|
112
129
|
* Check if content is pinned.
|
|
130
|
+
* @param cid CID to check
|
|
113
131
|
*/
|
|
114
132
|
async isPinned(cid) {
|
|
115
133
|
const cidObj = typeof cid === "string" ? CID.parse(cid) : cid;
|
|
@@ -117,6 +135,8 @@ var Pinner = class {
|
|
|
117
135
|
}
|
|
118
136
|
/**
|
|
119
137
|
* Update pin metadata.
|
|
138
|
+
* @param cid CID of the pin
|
|
139
|
+
* @param metadata Key-value metadata to set
|
|
120
140
|
*/
|
|
121
141
|
async setPinMetadata(cid, metadata) {
|
|
122
142
|
const cidObj = typeof cid === "string" ? CID.parse(cid) : cid;
|
|
@@ -124,6 +144,8 @@ var Pinner = class {
|
|
|
124
144
|
}
|
|
125
145
|
/**
|
|
126
146
|
* Remove a pin. The block may be deleted when garbage collection is run.
|
|
147
|
+
* @param cid CID to unpin
|
|
148
|
+
* @param options Abort options
|
|
127
149
|
*/
|
|
128
150
|
async unpin(cid, options) {
|
|
129
151
|
const cidObj = typeof cid === "string" ? CID.parse(cid) : cid;
|
|
@@ -132,6 +154,8 @@ var Pinner = class {
|
|
|
132
154
|
}
|
|
133
155
|
/**
|
|
134
156
|
* Remove a pin by request ID. The block may be deleted when garbage collection is run.
|
|
157
|
+
* @param requestId The request ID to remove
|
|
158
|
+
* @param options Abort options
|
|
135
159
|
*/
|
|
136
160
|
async unpinByRequestId(requestId, options) {
|
|
137
161
|
return this.pins.rmByRequestId(requestId, options);
|
package/dist/esm/pinner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pinner.js","names":[],"sources":["../../src/pinner.ts"],"sourcesContent":["import type { PinnerConfig } from \"./config\";\nimport { UploadManager } from \"./upload\";\nimport { PinClient } from \"./pin\";\nimport { IpnsClient } from \"./api/ipns\";\nimport { WebsitesClient } from \"./api/websites\";\nimport type { UploadMethodAndBuilder } from \"@/upload/builder\";\nimport { createUploadBuilderNamespace } from \"@/upload/builder\";\nimport type {\n UploadOperation,\n UploadOptions,\n UploadResult,\n} from \"@/types/upload\";\nimport type { OperationPollingOptions } from \"@lumeweb/portal-sdk\";\nimport type {\n AbortOptions,\n RemoteAddOptions,\n RemoteLsOptions,\n RemotePin,\n RemotePins,\n} from \"@/types/pin\";\nimport { CID } from \"multiformats/cid\";\n\nexport class Pinner {\n private uploadManager: UploadManager;\n private _pins: RemotePins;\n private _ipns: IpnsClient;\n private _websites: WebsitesClient;\n private _upload?: UploadMethodAndBuilder;\n\n constructor(config: PinnerConfig) {\n this.uploadManager = new UploadManager(config);\n this._pins = new PinClient(config);\n this._ipns = new IpnsClient(config);\n this._websites = new WebsitesClient(config);\n }\n\n /**\n * Access the remote pins interface.\n */\n get pins(): RemotePins {\n return this._pins;\n }\n\n /**\n * Access the IPNS interface for key management and publishing.\n */\n get ipns(): IpnsClient {\n return this._ipns;\n }\n\n /**\n * Access the websites interface for website configuration and management.\n */\n get websites(): WebsitesClient {\n return this._websites;\n }\n\n /**\n * Upload interface that works as both a method and a builder namespace.\n *\n * As a method: upload(file, options) -> UploadOperation\n * As a property: upload.file(), upload.json(), etc. -> Builder\n */\n get upload(): UploadMethodAndBuilder {\n if (!this._upload) {\n const builderNamespace = createUploadBuilderNamespace(this);\n const uploadMethod = async (\n file: File,\n options?: UploadOptions,\n ): Promise<UploadOperation> => {\n return this.uploadManager.upload(file, options);\n };\n\n this._upload = new Proxy(uploadMethod, {\n get(target, prop) {\n if (prop in builderNamespace) {\n return Reflect.get(builderNamespace, prop);\n }\n return Reflect.get(target, prop);\n },\n }) as UploadMethodAndBuilder;\n }\n return this._upload;\n }\n\n /**\n * Upload a file and wait for completion.\n * Convenience method for simple use cases where controls aren't needed.\n */\n async uploadAndWait(\n file: File,\n options?: UploadOptions,\n ): Promise<UploadResult> {\n const operation = await this.upload(file, options);\n return operation.result;\n }\n\n /**\n * Wait for an operation to complete or reach a settled state.\n * @param input Either an operation ID (number) or an UploadResult\n * @param options Polling options (interval, timeout, settledStates)\n * @returns UploadResult with operation status merged in\n */\n async waitForOperation(\n input: number | UploadResult,\n options?: OperationPollingOptions,\n ): Promise<UploadResult> {\n return this.uploadManager.waitForOperation(input, options);\n }\n\n /**\n * Upload a directory to IPFS.\n */\n async uploadDirectory(\n files: File[],\n options?: UploadOptions,\n ): Promise<UploadOperation> {\n return this.uploadManager.uploadDirectory(files, options);\n }\n\n /**\n * Upload a CAR file without preprocessing.\n * This is useful for passthrough of pre-generated CAR files.\n */\n async uploadCar(\n file: File | ReadableStream<Uint8Array>,\n options?: UploadOptions,\n ): Promise<UploadOperation> {\n return this.uploadManager.uploadCar(file, options);\n }\n\n /**\n * Pin existing content by CID.\n */\n async pinByHash(\n cid: string | CID,\n options?: RemoteAddOptions,\n ): Promise<AsyncGenerator<CID, void, undefined>> {\n const cidObj = typeof cid === \"string\" ? CID.parse(cid) : cid;\n return this.pins.add(cidObj, options);\n }\n\n /**\n * List pinned content.\n */\n async listPins(options?: RemoteLsOptions): Promise<RemotePin[]> {\n const pins: RemotePin[] = [];\n for await (const pin of this.pins.ls(options)) {\n pins.push(pin);\n }\n return pins;\n }\n\n /**\n * Get pin status.\n */\n async getPinStatus(cid: string | CID): Promise<RemotePin> {\n const cidObj = typeof cid === \"string\" ? CID.parse(cid) : cid;\n return this.pins.get(cidObj);\n }\n\n /**\n * Check if content is pinned.\n */\n async isPinned(cid: string | CID): Promise<boolean> {\n const cidObj = typeof cid === \"string\" ? CID.parse(cid) : cid;\n return this.pins.isPinned(cidObj);\n }\n\n /**\n * Update pin metadata.\n */\n async setPinMetadata(\n cid: string | CID,\n metadata: Record<string, string> | undefined,\n ): Promise<void> {\n const cidObj = typeof cid === \"string\" ? CID.parse(cid) : cid;\n return this.pins.setMetadata(cidObj, metadata);\n }\n\n /**\n * Remove a pin. The block may be deleted when garbage collection is run.\n */\n async unpin(cid: string | CID, options?: AbortOptions): Promise<void> {\n const cidObj = typeof cid === \"string\" ? CID.parse(cid) : cid;\n const generator = this.pins.rm(cidObj, options);\n for await (const _ of generator) {\n // Consume the generator to complete the unpin operation\n }\n }\n\n /**\n * Remove a pin by request ID. The block may be deleted when garbage collection is run.\n */\n async unpinByRequestId(requestId: string, options?: AbortOptions): Promise<void> {\n return this.pins.rmByRequestId(requestId, options);\n }\n\n /**\n * Destroy the client and cleanup resources.\n */\n destroy(): void {\n this.uploadManager.destroy();\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"pinner.js","names":[],"sources":["../../src/pinner.ts"],"sourcesContent":["import type { PinnerConfig } from \"./config\";\nimport { UploadManager } from \"./upload\";\nimport { PinClient } from \"./pin\";\nimport { IpnsClient } from \"./api/ipns\";\nimport { WebsitesClient } from \"./api/websites\";\nimport { JwtAuthManager, type AuthManager } from \"@/auth\";\nimport type { UploadMethodAndBuilder } from \"@/upload/builder\";\nimport { createUploadBuilderNamespace } from \"@/upload/builder\";\nimport type {\n UploadOperation,\n UploadOptions,\n UploadResult,\n} from \"@/types/upload\";\nimport type { OperationPollingOptions } from \"@lumeweb/portal-sdk\";\nimport type {\n AbortOptions,\n RemoteAddOptions,\n RemoteLsOptions,\n RemotePin,\n RemotePins,\n} from \"@/types/pin\";\nimport { CID } from \"multiformats/cid\";\n\nexport class Pinner {\n private uploadManager: UploadManager;\n private _pins: RemotePins;\n private _ipns: IpnsClient;\n private _websites: WebsitesClient;\n private _upload?: UploadMethodAndBuilder;\n private readonly auth: AuthManager;\n\n /**\n * Create a new Pinner SDK instance.\n * @param config SDK configuration object\n */\n constructor(config: PinnerConfig) {\n this.auth = new JwtAuthManager(config.jwt);\n this.uploadManager = new UploadManager(config, this.auth);\n this._pins = new PinClient(config, this.auth);\n this._ipns = new IpnsClient(config, this.auth);\n this._websites = new WebsitesClient(config, this.auth);\n }\n\n /**\n * Access the remote pins interface.\n */\n get pins(): RemotePins {\n return this._pins;\n }\n\n /**\n * Access the IPNS interface for key management and publishing.\n */\n get ipns(): IpnsClient {\n return this._ipns;\n }\n\n /**\n * Access the websites interface for website configuration and management.\n */\n get websites(): WebsitesClient {\n return this._websites;\n }\n\n /**\n * Upload interface that works as both a method and a builder namespace.\n *\n * As a method: upload(file, options) -> UploadOperation\n * As a property: upload.file(), upload.json(), etc. -> Builder\n */\n get upload(): UploadMethodAndBuilder {\n if (!this._upload) {\n const builderNamespace = createUploadBuilderNamespace(this);\n const uploadMethod = async (\n file: File,\n options?: UploadOptions,\n ): Promise<UploadOperation> => {\n return this.uploadManager.upload(file, options);\n };\n\n this._upload = new Proxy(uploadMethod, {\n get(target, prop) {\n if (prop in builderNamespace) {\n return Reflect.get(builderNamespace, prop);\n }\n return Reflect.get(target, prop);\n },\n }) as UploadMethodAndBuilder;\n }\n return this._upload;\n }\n\n /**\n * Upload a file and wait for completion.\n * Convenience method for simple use cases where controls aren't needed.\n * @param file The file to upload\n * @param options Upload configuration\n */\n async uploadAndWait(\n file: File,\n options?: UploadOptions,\n ): Promise<UploadResult> {\n const operation = await this.upload(file, options);\n return operation.result;\n }\n\n /**\n * Wait for an operation to complete or reach a settled state.\n * @param input Either an operation ID (number) or an UploadResult\n * @param options Polling options (interval, timeout, settledStates)\n * @returns UploadResult with operation status merged in\n */\n async waitForOperation(\n input: number | UploadResult,\n options?: OperationPollingOptions,\n ): Promise<UploadResult> {\n return this.uploadManager.waitForOperation(input, options);\n }\n\n /**\n * Upload a directory to IPFS.\n * @param files Array of files to upload as a directory\n * @param options Upload configuration\n */\n async uploadDirectory(\n files: File[],\n options?: UploadOptions,\n ): Promise<UploadOperation> {\n return this.uploadManager.uploadDirectory(files, options);\n }\n\n /**\n * Upload a CAR file without preprocessing.\n * This is useful for passthrough of pre-generated CAR files.\n * @param file CAR file or stream to upload\n * @param options Upload configuration\n */\n async uploadCar(\n file: File | ReadableStream<Uint8Array>,\n options?: UploadOptions,\n ): Promise<UploadOperation> {\n return this.uploadManager.uploadCar(file, options);\n }\n\n /**\n * Pin existing content by CID.\n * @param cid CID of content to pin (string or CID object)\n * @param options Remote add options\n */\n async pinByHash(\n cid: string | CID,\n options?: RemoteAddOptions,\n ): Promise<AsyncGenerator<CID, void, undefined>> {\n const cidObj = typeof cid === \"string\" ? CID.parse(cid) : cid;\n return this.pins.add(cidObj, options);\n }\n\n /**\n * List pinned content.\n * @param options List filtering options\n */\n async listPins(options?: RemoteLsOptions): Promise<RemotePin[]> {\n const pins: RemotePin[] = [];\n for await (const pin of this.pins.ls(options)) {\n pins.push(pin);\n }\n return pins;\n }\n\n /**\n * Get pin status.\n * @param cid CID of the pinned content to check\n */\n async getPinStatus(cid: string | CID): Promise<RemotePin> {\n const cidObj = typeof cid === \"string\" ? CID.parse(cid) : cid;\n return this.pins.get(cidObj);\n }\n\n /**\n * Check if content is pinned.\n * @param cid CID to check\n */\n async isPinned(cid: string | CID): Promise<boolean> {\n const cidObj = typeof cid === \"string\" ? CID.parse(cid) : cid;\n return this.pins.isPinned(cidObj);\n }\n\n /**\n * Update pin metadata.\n * @param cid CID of the pin\n * @param metadata Key-value metadata to set\n */\n async setPinMetadata(\n cid: string | CID,\n metadata: Record<string, string> | undefined,\n ): Promise<void> {\n const cidObj = typeof cid === \"string\" ? CID.parse(cid) : cid;\n return this.pins.setMetadata(cidObj, metadata);\n }\n\n /**\n * Remove a pin. The block may be deleted when garbage collection is run.\n * @param cid CID to unpin\n * @param options Abort options\n */\n async unpin(cid: string | CID, options?: AbortOptions): Promise<void> {\n const cidObj = typeof cid === \"string\" ? CID.parse(cid) : cid;\n const generator = this.pins.rm(cidObj, options);\n for await (const _ of generator) {\n // Consume the generator to complete the unpin operation\n }\n }\n\n /**\n * Remove a pin by request ID. The block may be deleted when garbage collection is run.\n * @param requestId The request ID to remove\n * @param options Abort options\n */\n async unpinByRequestId(requestId: string, options?: AbortOptions): Promise<void> {\n return this.pins.rmByRequestId(requestId, options);\n }\n\n /**\n * Destroy the client and cleanup resources.\n */\n destroy(): void {\n this.uploadManager.destroy();\n }\n}\n"],"mappings":";;;;;;;;;;;AAuBA,IAAa,SAAb,MAAoB;CAClB,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAiB;;;;;CAMjB,YAAY,QAAsB;AAChC,OAAK,OAAO,IAAI,eAAe,OAAO,IAAI;AAC1C,OAAK,gBAAgB,IAAI,cAAc,QAAQ,KAAK,KAAK;AACzD,OAAK,QAAQ,IAAI,UAAU,QAAQ,KAAK,KAAK;AAC7C,OAAK,QAAQ,IAAI,WAAW,QAAQ,KAAK,KAAK;AAC9C,OAAK,YAAY,IAAI,eAAe,QAAQ,KAAK,KAAK;;;;;CAMxD,IAAI,OAAmB;AACrB,SAAO,KAAK;;;;;CAMd,IAAI,OAAmB;AACrB,SAAO,KAAK;;;;;CAMd,IAAI,WAA2B;AAC7B,SAAO,KAAK;;;;;;;;CASd,IAAI,SAAiC;AACnC,MAAI,CAAC,KAAK,SAAS;GACjB,MAAM,mBAAmB,6BAA6B,KAAK;GAC3D,MAAM,eAAe,OACnB,MACA,YAC6B;AAC7B,WAAO,KAAK,cAAc,OAAO,MAAM,QAAQ;;AAGjD,QAAK,UAAU,IAAI,MAAM,cAAc,EACrC,IAAI,QAAQ,MAAM;AAChB,QAAI,QAAQ,iBACV,QAAO,QAAQ,IAAI,kBAAkB,KAAK;AAE5C,WAAO,QAAQ,IAAI,QAAQ,KAAK;MAEnC,CAAC;;AAEJ,SAAO,KAAK;;;;;;;;CASd,MAAM,cACJ,MACA,SACuB;AAEvB,UAAO,MADiB,KAAK,OAAO,MAAM,QAAQ,EACjC;;;;;;;;CASnB,MAAM,iBACJ,OACA,SACuB;AACvB,SAAO,KAAK,cAAc,iBAAiB,OAAO,QAAQ;;;;;;;CAQ5D,MAAM,gBACJ,OACA,SAC0B;AAC1B,SAAO,KAAK,cAAc,gBAAgB,OAAO,QAAQ;;;;;;;;CAS3D,MAAM,UACJ,MACA,SAC0B;AAC1B,SAAO,KAAK,cAAc,UAAU,MAAM,QAAQ;;;;;;;CAQpD,MAAM,UACJ,KACA,SAC+C;EAC/C,MAAM,SAAS,OAAO,QAAQ,WAAW,IAAI,MAAM,IAAI,GAAG;AAC1D,SAAO,KAAK,KAAK,IAAI,QAAQ,QAAQ;;;;;;CAOvC,MAAM,SAAS,SAAiD;EAC9D,MAAM,OAAoB,EAAE;AAC5B,aAAW,MAAM,OAAO,KAAK,KAAK,GAAG,QAAQ,CAC3C,MAAK,KAAK,IAAI;AAEhB,SAAO;;;;;;CAOT,MAAM,aAAa,KAAuC;EACxD,MAAM,SAAS,OAAO,QAAQ,WAAW,IAAI,MAAM,IAAI,GAAG;AAC1D,SAAO,KAAK,KAAK,IAAI,OAAO;;;;;;CAO9B,MAAM,SAAS,KAAqC;EAClD,MAAM,SAAS,OAAO,QAAQ,WAAW,IAAI,MAAM,IAAI,GAAG;AAC1D,SAAO,KAAK,KAAK,SAAS,OAAO;;;;;;;CAQnC,MAAM,eACJ,KACA,UACe;EACf,MAAM,SAAS,OAAO,QAAQ,WAAW,IAAI,MAAM,IAAI,GAAG;AAC1D,SAAO,KAAK,KAAK,YAAY,QAAQ,SAAS;;;;;;;CAQhD,MAAM,MAAM,KAAmB,SAAuC;EACpE,MAAM,SAAS,OAAO,QAAQ,WAAW,IAAI,MAAM,IAAI,GAAG;EAC1D,MAAM,YAAY,KAAK,KAAK,GAAG,QAAQ,QAAQ;AAC/C,aAAW,MAAM,KAAK;;;;;;;CAUxB,MAAM,iBAAiB,WAAmB,SAAuC;AAC/E,SAAO,KAAK,KAAK,cAAc,WAAW,QAAQ;;;;;CAMpD,UAAgB;AACd,OAAK,cAAc,SAAS"}
|
package/dist/esm/types/pin.d.ts
CHANGED
|
@@ -6,34 +6,76 @@ import { Status } from "@ipfs-shipyard/pinning-service-client";
|
|
|
6
6
|
* Options that can be passed to abort async operations
|
|
7
7
|
*/
|
|
8
8
|
interface AbortOptions {
|
|
9
|
+
/**
|
|
10
|
+
* AbortSignal to cancel the operation
|
|
11
|
+
*/
|
|
9
12
|
signal?: AbortSignal;
|
|
10
13
|
}
|
|
11
14
|
/**
|
|
12
15
|
* Allows passing extra options accepted by the remote pinning service
|
|
13
16
|
*/
|
|
14
17
|
interface RemoteAddOptions extends AbortOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Name for the pin or filter
|
|
20
|
+
*/
|
|
15
21
|
name?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Key-value metadata to attach to the pin
|
|
24
|
+
*/
|
|
16
25
|
metadata?: Record<string, string>;
|
|
26
|
+
/**
|
|
27
|
+
* Origin addresses for the pinned content
|
|
28
|
+
*/
|
|
17
29
|
origins?: string[];
|
|
18
30
|
}
|
|
19
31
|
/**
|
|
20
32
|
* Allows passing extra options accepted by the remote pinning service
|
|
21
33
|
*/
|
|
22
34
|
interface RemoteLsOptions extends AbortOptions {
|
|
35
|
+
/**
|
|
36
|
+
* Name for the pin or filter
|
|
37
|
+
*/
|
|
23
38
|
name?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Current status (queued, pinning, pinned, failed)
|
|
41
|
+
*/
|
|
24
42
|
status?: Status[];
|
|
43
|
+
/**
|
|
44
|
+
* Maximum number of results per page
|
|
45
|
+
*/
|
|
25
46
|
limit?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Pagination cursor for listing results
|
|
49
|
+
*/
|
|
26
50
|
cursor?: string;
|
|
27
51
|
}
|
|
28
52
|
/**
|
|
29
53
|
* Includes extra metadata supported by the remote pinning service
|
|
30
54
|
*/
|
|
31
55
|
interface RemotePin {
|
|
56
|
+
/**
|
|
57
|
+
* IPFS Content Identifier
|
|
58
|
+
*/
|
|
32
59
|
cid: CID;
|
|
60
|
+
/**
|
|
61
|
+
* Name for the pin or filter
|
|
62
|
+
*/
|
|
33
63
|
name?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Current status (queued, pinning, pinned, failed)
|
|
66
|
+
*/
|
|
34
67
|
status: Status;
|
|
68
|
+
/**
|
|
69
|
+
* ISO timestamp of creation
|
|
70
|
+
*/
|
|
35
71
|
created: Date;
|
|
72
|
+
/**
|
|
73
|
+
* Size in bytes
|
|
74
|
+
*/
|
|
36
75
|
size?: number;
|
|
76
|
+
/**
|
|
77
|
+
* Key-value metadata to attach to the pin
|
|
78
|
+
*/
|
|
37
79
|
metadata?: Record<string, string>;
|
|
38
80
|
}
|
|
39
81
|
/**
|
|
@@ -9,8 +9,10 @@ import defer from "p-defer";
|
|
|
9
9
|
//#region src/upload/base-upload.ts
|
|
10
10
|
var BaseUploadHandler = class {
|
|
11
11
|
config;
|
|
12
|
-
|
|
12
|
+
auth;
|
|
13
|
+
constructor(config, auth) {
|
|
13
14
|
this.config = config;
|
|
15
|
+
this.auth = auth;
|
|
14
16
|
}
|
|
15
17
|
async upload(input, options) {
|
|
16
18
|
const normalized = normalizeUploadInput(input, options);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-upload.js","names":["#setupUppyHandlers","#addFileToUppy","#startUpload","#createUploadOperation","#extractErrorMessage","#normalizeData","#normalizeDataForNode","#normalizeDataForBrowser"],"sources":["../../../src/upload/base-upload.ts"],"sourcesContent":["import Uppy from \"@uppy/core\";\nimport { default as defer } from \"p-defer\";\nimport type { Readable } from \"stream\";\n\nimport type { PinnerConfig } from \"../config\";\nimport type {\n UploadInput,\n UploadOperation,\n UploadOptions,\n UploadProgress,\n UploadResult,\n} from \"@/types/upload\";\nimport { UploadResultSymbol } from \"@/types/upload\";\nimport type { PostUploadResponse } from \"@/api/generated/schemas\";\nimport { normalizeUploadInput, type UploadInputObject } from \"./normalize\";\nimport {\n fileToReadableStream,\n readableStreamToNodeStream,\n} from \"@/utils/stream\";\nimport { isNodeEnvironment } from \"@/utils/env\";\nimport { UPLOAD_SOURCE_TUS, UPLOAD_SOURCE_XHR } from \"./constants\";\n\n// Node.js Readable stream with size property for Uppy compatibility\ntype NodeStreamWithSize = Readable & { size: number | null };\n\nexport abstract class BaseUploadHandler {\n protected config: Required<PinnerConfig>;\n\n constructor(config: PinnerConfig) {\n this.config = config as Required<PinnerConfig>;\n }\n\n async upload(\n input: UploadInput | UploadInputObject,\n options?: UploadOptions,\n ): Promise<UploadOperation> {\n const normalized = normalizeUploadInput(input, options);\n const uppy = new Uppy();\n const { fileId, resultPromise, progress } = this.#setupUppyHandlers(\n uppy,\n normalized,\n options,\n );\n\n await this.#addFileToUppy(uppy, normalized, normalized.size);\n this.#startUpload(uppy, options);\n\n return this.#createUploadOperation(uppy, fileId, resultPromise, progress);\n }\n\n #setupUppyHandlers(\n uppy: Uppy,\n normalized: { size: number },\n options?: UploadOptions,\n ): {\n fileId: string | null;\n resultPromise: Promise<UploadResult>;\n progress: UploadProgress;\n } {\n let fileId: string | null = null;\n let hasRejected = false;\n\n const progress: UploadProgress = {\n percentage: 0,\n bytesUploaded: 0,\n bytesTotal: normalized.size,\n };\n\n const {\n promise: resultPromise,\n resolve: resolveResult,\n reject: rejectResult,\n } = defer<UploadResult>();\n\n const handleError = (error: Error) => {\n if (hasRejected) return;\n hasRejected = true;\n options?.onError?.(error);\n rejectResult(error);\n };\n\n this.configurePlugin(uppy);\n\n uppy.on(\"progress\", (progressBytes) => {\n progress.bytesUploaded = progressBytes;\n progress.percentage = (progressBytes / progress.bytesTotal) * 100;\n options?.onProgress?.(progress);\n });\n\n uppy.on(\"upload-success\", (_file, result) => {\n if (hasRejected) return;\n const uploadResult = this.parseResult(result);\n options?.onComplete?.(uploadResult);\n resolveResult(uploadResult);\n });\n\n uppy.on(\"error\", (error) => {\n handleError(new Error(this.#extractErrorMessage(error)));\n });\n\n uppy.on(\"file-added\", (file) => {\n fileId = file.id;\n });\n\n return { fileId, resultPromise, progress };\n }\n\n #extractErrorMessage(error: unknown): string {\n let errorMessage = \"Upload fainormalizeDataled\";\n\n if (!error) return errorMessage;\n\n if (typeof error === \"string\") {\n errorMessage = error;\n } else if (error instanceof Error) {\n errorMessage = error.message;\n } else if ((error as any).message) {\n errorMessage = (error as any).message;\n } else if (\n (error as any).toString &&\n typeof (error as any).toString === \"function\"\n ) {\n errorMessage = (error as any).toString();\n }\n\n // Try to extract error from response (XHRUpload/TusPlugin)\n const errorObj = error as any;\n if (errorObj?.xhr?.response) {\n try {\n const response =\n typeof errorObj.xhr.response === \"string\"\n ? JSON.parse(errorObj.xhr.response)\n : errorObj.xhr.response;\n if (response.error) {\n errorMessage = response.error;\n } else if (response.message) {\n errorMessage = response.message;\n }\n } catch {\n // If parsing fails, use the original error message\n }\n }\n\n return errorMessage;\n }\n\n async #normalizeData(\n data: File | ReadableStream<Uint8Array>,\n ): Promise<File | ReadableStream<Uint8Array> | NodeStreamWithSize | Blob> {\n if (isNodeEnvironment()) {\n return this.#normalizeDataForNode(data);\n }\n return this.#normalizeDataForBrowser(data);\n }\n\n async #normalizeDataForBrowser(\n data: File | ReadableStream<Uint8Array>,\n ): Promise<File | Blob | ReadableStream<Uint8Array>> {\n // TUS plugin requires File, Blob, or Reader in browser\n if (this.getUploadSource() === UPLOAD_SOURCE_TUS) {\n if (data instanceof ReadableStream) {\n const { streamToBlobViaResponse } = await import(\"@/utils/stream\");\n return streamToBlobViaResponse(data);\n }\n return data;\n }\n\n // XHRUpload handles File/Blob directly\n return data;\n }\n\n async #normalizeDataForNode(\n data: File | ReadableStream<Uint8Array>,\n ): Promise<NodeStreamWithSize | File | Blob | ReadableStream<Uint8Array>> {\n // XHRUpload with formData: true requires Blob/File for FormData.append()\n // Do not convert to Node.js stream for XHRUpload\n if (this.getUploadSource() === UPLOAD_SOURCE_XHR) {\n return data;\n }\n\n // Convert File to ReadableStream without loading entire blob into memory\n if (data instanceof File) {\n const stream = fileToReadableStream(data);\n // Convert to Node.js stream for tus-js-client\n const nodeStream = await readableStreamToNodeStream(stream);\n return nodeStream as NodeStreamWithSize;\n }\n\n // In Node.js, convert ReadableStream to Node.js stream.Readable for tus-js-client's NodeFileReader\n if (data instanceof ReadableStream) {\n const nodeStream = await readableStreamToNodeStream(data);\n // Add size property to satisfy Uppy's type requirements\n return nodeStream as NodeStreamWithSize;\n }\n\n return data;\n }\n\n async #addFileToUppy(\n uppy: Uppy,\n normalized: {\n data: File | ReadableStream<Uint8Array>;\n name: string;\n type: string;\n },\n size?: number,\n ): Promise<void> {\n const fileData = await this.#normalizeData(normalized.data);\n const fileOptions = {\n source: this.getUploadSource(),\n name: normalized.name,\n type: normalized.type,\n data: fileData as any,\n };\n\n // Add file to Uppy first\n // Note: Uppy accepts any data type as it defers to the drivers\n const fileId = uppy.addFile(fileOptions);\n\n // Set TUS upload size if provided\n // In Node.js, streams need explicit size for tus-js-client\n // In browser, Uppy's TUS plugin may not derive size from Blob automatically\n if (\n this.getUploadSource() === UPLOAD_SOURCE_TUS &&\n size !== undefined &&\n size > 0\n ) {\n uppy.setFileState(fileId, {\n tus: { uploadSize: size },\n });\n }\n }\n\n #startUpload(uppy: Uppy, options?: UploadOptions): void {\n uppy.upload().catch((error) => {\n options?.onError?.(new Error(this.#extractErrorMessage(error)));\n });\n }\n\n #createUploadOperation(\n uppy: Uppy,\n fileId: string | null,\n resultPromise: Promise<UploadResult>,\n progress: UploadProgress,\n ): UploadOperation {\n return {\n cancel: () => {\n uppy.cancelAll();\n },\n pause: () => {\n if (fileId) {\n uppy.pauseResume(fileId);\n }\n },\n resume: () => {\n if (fileId) {\n uppy.pauseResume(fileId);\n }\n },\n result: resultPromise,\n progress: Object.freeze({ ...progress }),\n };\n }\n\n destroy(): void {\n // No-op since each upload creates its own Uppy instance\n }\n\n protected abstract configurePlugin(uppy: Uppy): void;\n protected abstract parseResult(result: unknown): UploadResult;\n protected abstract getUploadSource(): string;\n\n protected mapUploadResponse(\n body: unknown,\n uploadId: string,\n overrides?: Partial<UploadResult>,\n ): UploadResult {\n if (body && typeof body === \"object\" && \"CID\" in body) {\n const { CID } = body as PostUploadResponse;\n return {\n id: uploadId,\n cid: CID,\n name: \"\",\n size: 0,\n mimeType: \"\",\n createdAt: new Date(),\n numberOfFiles: 1,\n [UploadResultSymbol]: true,\n ...overrides,\n };\n }\n\n return {\n id: uploadId,\n name: \"\",\n size: 0,\n mimeType: \"\",\n createdAt: new Date(),\n numberOfFiles: 1,\n [UploadResultSymbol]: true,\n ...overrides,\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"base-upload.js","names":["#setupUppyHandlers","#addFileToUppy","#startUpload","#createUploadOperation","#extractErrorMessage","#normalizeData","#normalizeDataForNode","#normalizeDataForBrowser"],"sources":["../../../src/upload/base-upload.ts"],"sourcesContent":["import Uppy from \"@uppy/core\";\nimport { default as defer } from \"p-defer\";\nimport type { Readable } from \"stream\";\n\nimport type { PinnerConfig } from \"../config\";\nimport type { AuthManager } from \"@/auth\";\nimport type {\n UploadInput,\n UploadOperation,\n UploadOptions,\n UploadProgress,\n UploadResult,\n} from \"@/types/upload\";\nimport { UploadResultSymbol } from \"@/types/upload\";\nimport type { PostUploadResponse } from \"@/api/generated/schemas\";\nimport { normalizeUploadInput, type UploadInputObject } from \"./normalize\";\nimport {\n fileToReadableStream,\n readableStreamToNodeStream,\n} from \"@/utils/stream\";\nimport { isNodeEnvironment } from \"@/utils/env\";\nimport { UPLOAD_SOURCE_TUS, UPLOAD_SOURCE_XHR } from \"./constants\";\n\n// Node.js Readable stream with size property for Uppy compatibility\ntype NodeStreamWithSize = Readable & { size: number | null };\n\nexport abstract class BaseUploadHandler {\n protected config: Required<PinnerConfig>;\n protected readonly auth: AuthManager;\n\n constructor(config: PinnerConfig, auth: AuthManager) {\n this.config = config as Required<PinnerConfig>;\n this.auth = auth;\n }\n\n async upload(\n input: UploadInput | UploadInputObject,\n options?: UploadOptions,\n ): Promise<UploadOperation> {\n const normalized = normalizeUploadInput(input, options);\n const uppy = new Uppy();\n const { fileId, resultPromise, progress } = this.#setupUppyHandlers(\n uppy,\n normalized,\n options,\n );\n\n await this.#addFileToUppy(uppy, normalized, normalized.size);\n this.#startUpload(uppy, options);\n\n return this.#createUploadOperation(uppy, fileId, resultPromise, progress);\n }\n\n #setupUppyHandlers(\n uppy: Uppy,\n normalized: { size: number },\n options?: UploadOptions,\n ): {\n fileId: string | null;\n resultPromise: Promise<UploadResult>;\n progress: UploadProgress;\n } {\n let fileId: string | null = null;\n let hasRejected = false;\n\n const progress: UploadProgress = {\n percentage: 0,\n bytesUploaded: 0,\n bytesTotal: normalized.size,\n };\n\n const {\n promise: resultPromise,\n resolve: resolveResult,\n reject: rejectResult,\n } = defer<UploadResult>();\n\n const handleError = (error: Error) => {\n if (hasRejected) return;\n hasRejected = true;\n options?.onError?.(error);\n rejectResult(error);\n };\n\n this.configurePlugin(uppy);\n\n uppy.on(\"progress\", (progressBytes) => {\n progress.bytesUploaded = progressBytes;\n progress.percentage = (progressBytes / progress.bytesTotal) * 100;\n options?.onProgress?.(progress);\n });\n\n uppy.on(\"upload-success\", (_file, result) => {\n if (hasRejected) return;\n const uploadResult = this.parseResult(result);\n options?.onComplete?.(uploadResult);\n resolveResult(uploadResult);\n });\n\n uppy.on(\"error\", (error) => {\n handleError(new Error(this.#extractErrorMessage(error)));\n });\n\n uppy.on(\"file-added\", (file) => {\n fileId = file.id;\n });\n\n return { fileId, resultPromise, progress };\n }\n\n #extractErrorMessage(error: unknown): string {\n let errorMessage = \"Upload fainormalizeDataled\";\n\n if (!error) return errorMessage;\n\n if (typeof error === \"string\") {\n errorMessage = error;\n } else if (error instanceof Error) {\n errorMessage = error.message;\n } else if ((error as any).message) {\n errorMessage = (error as any).message;\n } else if (\n (error as any).toString &&\n typeof (error as any).toString === \"function\"\n ) {\n errorMessage = (error as any).toString();\n }\n\n // Try to extract error from response (XHRUpload/TusPlugin)\n const errorObj = error as any;\n if (errorObj?.xhr?.response) {\n try {\n const response =\n typeof errorObj.xhr.response === \"string\"\n ? JSON.parse(errorObj.xhr.response)\n : errorObj.xhr.response;\n if (response.error) {\n errorMessage = response.error;\n } else if (response.message) {\n errorMessage = response.message;\n }\n } catch {\n // If parsing fails, use the original error message\n }\n }\n\n return errorMessage;\n }\n\n async #normalizeData(\n data: File | ReadableStream<Uint8Array>,\n ): Promise<File | ReadableStream<Uint8Array> | NodeStreamWithSize | Blob> {\n if (isNodeEnvironment()) {\n return this.#normalizeDataForNode(data);\n }\n return this.#normalizeDataForBrowser(data);\n }\n\n async #normalizeDataForBrowser(\n data: File | ReadableStream<Uint8Array>,\n ): Promise<File | Blob | ReadableStream<Uint8Array>> {\n // TUS plugin requires File, Blob, or Reader in browser\n if (this.getUploadSource() === UPLOAD_SOURCE_TUS) {\n if (data instanceof ReadableStream) {\n const { streamToBlobViaResponse } = await import(\"@/utils/stream\");\n return streamToBlobViaResponse(data);\n }\n return data;\n }\n\n // XHRUpload handles File/Blob directly\n return data;\n }\n\n async #normalizeDataForNode(\n data: File | ReadableStream<Uint8Array>,\n ): Promise<NodeStreamWithSize | File | Blob | ReadableStream<Uint8Array>> {\n // XHRUpload with formData: true requires Blob/File for FormData.append()\n // Do not convert to Node.js stream for XHRUpload\n if (this.getUploadSource() === UPLOAD_SOURCE_XHR) {\n return data;\n }\n\n // Convert File to ReadableStream without loading entire blob into memory\n if (data instanceof File) {\n const stream = fileToReadableStream(data);\n // Convert to Node.js stream for tus-js-client\n const nodeStream = await readableStreamToNodeStream(stream);\n return nodeStream as NodeStreamWithSize;\n }\n\n // In Node.js, convert ReadableStream to Node.js stream.Readable for tus-js-client's NodeFileReader\n if (data instanceof ReadableStream) {\n const nodeStream = await readableStreamToNodeStream(data);\n // Add size property to satisfy Uppy's type requirements\n return nodeStream as NodeStreamWithSize;\n }\n\n return data;\n }\n\n async #addFileToUppy(\n uppy: Uppy,\n normalized: {\n /**\n * List of file manager items\n */\n data: File | ReadableStream<Uint8Array>;\n /**\n * Name for the pin or filter\n */\n name: string;\n /**\n * MIME type or content type\n */\n type: string;\n },\n size?: number,\n ): Promise<void> {\n const fileData = await this.#normalizeData(normalized.data);\n const fileOptions = {\n source: this.getUploadSource(),\n name: normalized.name,\n type: normalized.type,\n data: fileData as any,\n };\n\n // Add file to Uppy first\n // Note: Uppy accepts any data type as it defers to the drivers\n const fileId = uppy.addFile(fileOptions);\n\n // Set TUS upload size if provided\n // In Node.js, streams need explicit size for tus-js-client\n // In browser, Uppy's TUS plugin may not derive size from Blob automatically\n if (\n this.getUploadSource() === UPLOAD_SOURCE_TUS &&\n size !== undefined &&\n size > 0\n ) {\n uppy.setFileState(fileId, {\n tus: { uploadSize: size },\n });\n }\n }\n\n #startUpload(uppy: Uppy, options?: UploadOptions): void {\n uppy.upload().catch((error) => {\n options?.onError?.(new Error(this.#extractErrorMessage(error)));\n });\n }\n\n #createUploadOperation(\n uppy: Uppy,\n fileId: string | null,\n resultPromise: Promise<UploadResult>,\n progress: UploadProgress,\n ): UploadOperation {\n return {\n cancel: () => {\n uppy.cancelAll();\n },\n pause: () => {\n if (fileId) {\n uppy.pauseResume(fileId);\n }\n },\n resume: () => {\n if (fileId) {\n uppy.pauseResume(fileId);\n }\n },\n result: resultPromise,\n progress: Object.freeze({ ...progress }),\n };\n }\n\n destroy(): void {\n // No-op since each upload creates its own Uppy instance\n }\n\n protected abstract configurePlugin(uppy: Uppy): void;\n protected abstract parseResult(result: unknown): UploadResult;\n protected abstract getUploadSource(): string;\n\n protected mapUploadResponse(\n body: unknown,\n uploadId: string,\n overrides?: Partial<UploadResult>,\n ): UploadResult {\n if (body && typeof body === \"object\" && \"CID\" in body) {\n const { CID } = body as PostUploadResponse;\n return {\n id: uploadId,\n cid: CID,\n name: \"\",\n size: 0,\n mimeType: \"\",\n createdAt: new Date(),\n numberOfFiles: 1,\n [UploadResultSymbol]: true,\n ...overrides,\n };\n }\n\n return {\n id: uploadId,\n name: \"\",\n size: 0,\n mimeType: \"\",\n createdAt: new Date(),\n numberOfFiles: 1,\n [UploadResultSymbol]: true,\n ...overrides,\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -55,5 +55,5 @@ declare class UploadBuilderNamespace {
|
|
|
55
55
|
*/
|
|
56
56
|
type UploadMethodAndBuilder = ((file: File, options?: UploadOptions) => Promise<UploadOperation>) & UploadBuilderNamespace;
|
|
57
57
|
//#endregion
|
|
58
|
-
export { UploadMethodAndBuilder };
|
|
58
|
+
export { UploadBuilderNamespace, UploadMethodAndBuilder };
|
|
59
59
|
//# sourceMappingURL=builder.d.ts.map
|
package/dist/esm/upload/car.d.ts
CHANGED
|
@@ -2,13 +2,31 @@ import { Datastore } from "interface-datastore";
|
|
|
2
2
|
|
|
3
3
|
//#region src/upload/car.d.ts
|
|
4
4
|
interface CarPreprocessOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Name for the pin or filter
|
|
7
|
+
*/
|
|
5
8
|
name?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Progress callback for preprocessing
|
|
11
|
+
*/
|
|
6
12
|
onProgress?: (percentage: number) => void;
|
|
13
|
+
/**
|
|
14
|
+
* AbortSignal to cancel the operation
|
|
15
|
+
*/
|
|
7
16
|
signal?: AbortSignal;
|
|
8
17
|
}
|
|
9
18
|
interface CarPreprocessResult {
|
|
19
|
+
/**
|
|
20
|
+
* Preprocessed CAR data as a readable stream
|
|
21
|
+
*/
|
|
10
22
|
carStream: ReadableStream<Uint8Array>;
|
|
23
|
+
/**
|
|
24
|
+
* Root CID of the CAR file
|
|
25
|
+
*/
|
|
11
26
|
rootCid: string;
|
|
27
|
+
/**
|
|
28
|
+
* Size in bytes
|
|
29
|
+
*/
|
|
12
30
|
size: bigint;
|
|
13
31
|
}
|
|
14
32
|
declare function preprocessToCar(input: File | ReadableStream<Uint8Array> | File[], options?: CarPreprocessOptions): Promise<CarPreprocessResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"car.js","names":["createUnstorageDatastore"],"sources":["../../../src/upload/car.ts"],"sourcesContent":["import { car } from \"@helia/car\";\nimport { createHeliaHTTP } from \"@helia/http\";\nimport { unixfs } from \"@helia/unixfs\";\nimport {\n createBlockstore,\n createDatastore as createUnstorageDatastore,\n} from \"@/blockstore\";\nimport type { CID } from \"multiformats/cid\";\nimport { CarReader } from \"@ipld/car\";\nimport type { Datastore } from \"interface-datastore\";\n\nimport {\n asyncGeneratorToReadableStream,\n calculateStreamSize,\n readableStreamToAsyncIterable,\n streamToBlob,\n} from \"@/utils/stream\";\nimport { FILE_EXTENSION_CAR, MIME_TYPE_CAR } from \"@/types/mime-types\";\n\nexport interface CarPreprocessOptions {\n name?: string;\n onProgress?: (percentage: number) => void;\n signal?: AbortSignal;\n}\n\nexport interface CarPreprocessResult {\n carStream: ReadableStream<Uint8Array>;\n rootCid: string;\n size: bigint;\n}\n\nexport interface CarConfig {\n /**\n * Custom datastore instance for Helia.\n * If provided, this datastore will be used directly without creating one from storage.\n * Highest priority - takes precedence over storage and datastoreName.\n */\n datastore?: Datastore;\n\n /**\n * Custom base name for Helia storage.\n * Passed as the `base` option to both blockstore and datastore storage instances.\n * Only used when datastore is not provided.\n * @default \"pinner-helia-data\"\n */\n datastoreName?: string;\n}\n\nlet helia: any = null;\nlet blockstore: any = null;\nlet datastore: any = null;\nlet config: CarConfig = {};\n\nexport function configureCar(carConfig: CarConfig) {\n config = carConfig;\n}\n\nasync function getHelia() {\n if (helia) return helia;\n\n const BlockstoreClass = createBlockstore();\n const DatastoreClass = createUnstorageDatastore();\n\n blockstore = new BlockstoreClass({\n prefix: \"pinner-helia-blocks\",\n base: config.datastoreName,\n });\n datastore =\n config.datastore ||\n new DatastoreClass({\n prefix: \"pinner-helia-data\",\n base: config.datastoreName,\n });\n\n helia = await createHeliaHTTP({\n blockstore,\n datastore,\n });\n\n return helia;\n}\n\nasync function cleanupHelia() {\n if (datastore?.close) {\n await datastore.close();\n }\n helia = null;\n blockstore = null;\n datastore = null;\n}\n\nasync function* fileSource(\n files: File[],\n onProgress?: (percentage: number) => void,\n signal?: AbortSignal,\n): AsyncGenerator<{\n content: AsyncIterable<Uint8Array> | undefined;\n path: string;\n}> {\n const seenDirs = new Set<string>();\n let totalBytes = 0n;\n let processedBytes = 0n;\n\n for (const file of files) {\n totalBytes += BigInt(file.size);\n }\n\n for (const file of files) {\n if (signal?.aborted) {\n throw new Error(\"Aborted\");\n }\n\n const fullPath = (file as any).webkitRelativePath ?? file.name;\n\n if (fullPath.includes(\"/.\")) {\n continue;\n }\n\n const parts = fullPath.split(\"/\").filter((part: string) => part.length > 0);\n\n for (let i = 1; i < parts.length; i++) {\n if (signal?.aborted) {\n throw new Error(\"Aborted\");\n }\n\n const dirPath = parts.slice(0, i).join(\"/\");\n\n if (!seenDirs.has(dirPath)) {\n seenDirs.add(dirPath);\n yield {\n content: (async function* () {})(),\n path: dirPath,\n };\n }\n }\n\n yield {\n content: readableStreamToAsyncIterable(file.stream()),\n path: fullPath,\n };\n\n if (onProgress && totalBytes > 0n) {\n processedBytes += BigInt(file.size);\n const progressPercent = Number((processedBytes * 100n) / totalBytes);\n onProgress(progressPercent);\n }\n }\n}\n\nexport async function preprocessToCar(\n input: File | ReadableStream<Uint8Array> | File[],\n options?: CarPreprocessOptions,\n): Promise<CarPreprocessResult> {\n let files: File[];\n\n if (input instanceof ReadableStream) {\n const [streamForSize, streamForFile] = input.tee();\n const size = await calculateStreamSize(streamForSize, options?.signal);\n const streamBlob = await streamToBlob(\n streamForFile,\n \"application/octet-stream\",\n );\n files = [\n new File([streamBlob], options?.name || \"upload\", {\n type: streamBlob.type,\n }),\n ];\n } else if (Array.isArray(input)) {\n files = input;\n } else {\n files = [input];\n }\n\n // wrapWithDirectory must be true for directory uploads (File[] input) so the\n // last addAll yield is the root directory CID, not a child file/subdirectory.\n // Single files don't need wrapping. Matches Go SDK wrapInDir behavior.\n const wrapWithDirectory = Array.isArray(input);\n\n const heliaInstance = await getHelia();\n const fs = unixfs(heliaInstance);\n const c = car(heliaInstance);\n\n let rootCid: CID | undefined;\n let blocksCount = 0n;\n\n const src = fileSource(files, options?.onProgress, options?.signal);\n\n let hasFiles = false;\n for await (const result of fs.addAll(src, {\n cidVersion: 1,\n rawLeaves: false,\n wrapWithDirectory,\n signal: options?.signal,\n onProgress(event) {\n if (event.type === \"blocks:put:blockstore:put\") {\n blocksCount++;\n }\n },\n })) {\n if (options?.signal?.aborted) {\n throw new Error(\"Aborted\");\n }\n rootCid = result.cid;\n hasFiles = true;\n }\n\n if (!hasFiles || !rootCid) {\n throw new Error(\"No files to process\");\n }\n\n // c.export() now returns an async generator directly (was renamed from 'stream')\n const carAsyncGenerator = c.export(rootCid!, { signal: options?.signal });\n const carStream = asyncGeneratorToReadableStream(carAsyncGenerator);\n\n // Use stream tee to create two identical streams - one for size calculation, one for processing\n const [streamForSize, streamForProcessing] = carStream.tee();\n\n const size = await calculateStreamSize(streamForSize, options?.signal);\n\n return {\n carStream: streamForProcessing,\n rootCid: rootCid!.toString(),\n size,\n };\n}\n\nexport async function isCarFile(file: File): Promise<boolean> {\n if (file.type !== MIME_TYPE_CAR && !file.name.endsWith(FILE_EXTENSION_CAR)) {\n return false;\n }\n\n try {\n const iterable = readableStreamToAsyncIterable(file.stream());\n const reader = await CarReader.fromIterable(iterable);\n const roots = await reader.getRoots();\n return roots.length > 0;\n } catch {\n return false;\n }\n}\n\nexport async function destroyCarPreprocessor() {\n await cleanupHelia();\n}\n"],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"car.js","names":["createUnstorageDatastore"],"sources":["../../../src/upload/car.ts"],"sourcesContent":["import { car } from \"@helia/car\";\nimport { createHeliaHTTP } from \"@helia/http\";\nimport { unixfs } from \"@helia/unixfs\";\nimport {\n createBlockstore,\n createDatastore as createUnstorageDatastore,\n} from \"@/blockstore\";\nimport type { CID } from \"multiformats/cid\";\nimport { CarReader } from \"@ipld/car\";\nimport type { Datastore } from \"interface-datastore\";\n\nimport {\n asyncGeneratorToReadableStream,\n calculateStreamSize,\n readableStreamToAsyncIterable,\n streamToBlob,\n} from \"@/utils/stream\";\nimport { FILE_EXTENSION_CAR, MIME_TYPE_CAR } from \"@/types/mime-types\";\n\nexport interface CarPreprocessOptions {\n /**\n * Name for the pin or filter\n */\n name?: string;\n /**\n * Progress callback for preprocessing\n */\n onProgress?: (percentage: number) => void;\n /**\n * AbortSignal to cancel the operation\n */\n signal?: AbortSignal;\n}\n\nexport interface CarPreprocessResult {\n /**\n * Preprocessed CAR data as a readable stream\n */\n carStream: ReadableStream<Uint8Array>;\n /**\n * Root CID of the CAR file\n */\n rootCid: string;\n /**\n * Size in bytes\n */\n size: bigint;\n}\n\nexport interface CarConfig {\n /**\n * Custom datastore instance for Helia.\n * If provided, this datastore will be used directly without creating one from storage.\n * Highest priority - takes precedence over storage and datastoreName.\n */\n datastore?: Datastore;\n\n /**\n * Custom base name for Helia storage.\n * Passed as the `base` option to both blockstore and datastore storage instances.\n * Only used when datastore is not provided.\n * @default \"pinner-helia-data\"\n */\n datastoreName?: string;\n}\n\nlet helia: any = null;\nlet blockstore: any = null;\nlet datastore: any = null;\nlet config: CarConfig = {};\n\nexport function configureCar(carConfig: CarConfig) {\n config = carConfig;\n}\n\nasync function getHelia() {\n if (helia) return helia;\n\n const BlockstoreClass = createBlockstore();\n const DatastoreClass = createUnstorageDatastore();\n\n blockstore = new BlockstoreClass({\n prefix: \"pinner-helia-blocks\",\n base: config.datastoreName,\n });\n datastore =\n config.datastore ||\n new DatastoreClass({\n prefix: \"pinner-helia-data\",\n base: config.datastoreName,\n });\n\n helia = await createHeliaHTTP({\n blockstore,\n datastore,\n });\n\n return helia;\n}\n\nasync function cleanupHelia() {\n if (datastore?.close) {\n await datastore.close();\n }\n helia = null;\n blockstore = null;\n datastore = null;\n}\n\nasync function* fileSource(\n files: File[],\n onProgress?: (percentage: number) => void,\n signal?: AbortSignal,\n): AsyncGenerator<{\n content: AsyncIterable<Uint8Array> | undefined;\n /**\n * File path within the pin\n */\n path: string;\n}> {\n const seenDirs = new Set<string>();\n let totalBytes = 0n;\n let processedBytes = 0n;\n\n for (const file of files) {\n totalBytes += BigInt(file.size);\n }\n\n for (const file of files) {\n if (signal?.aborted) {\n throw new Error(\"Aborted\");\n }\n\n const fullPath = (file as any).webkitRelativePath ?? file.name;\n\n if (fullPath.includes(\"/.\")) {\n continue;\n }\n\n const parts = fullPath.split(\"/\").filter((part: string) => part.length > 0);\n\n for (let i = 1; i < parts.length; i++) {\n if (signal?.aborted) {\n throw new Error(\"Aborted\");\n }\n\n const dirPath = parts.slice(0, i).join(\"/\");\n\n if (!seenDirs.has(dirPath)) {\n seenDirs.add(dirPath);\n yield {\n content: (async function* () {})(),\n path: dirPath,\n };\n }\n }\n\n yield {\n content: readableStreamToAsyncIterable(file.stream()),\n path: fullPath,\n };\n\n if (onProgress && totalBytes > 0n) {\n processedBytes += BigInt(file.size);\n const progressPercent = Number((processedBytes * 100n) / totalBytes);\n onProgress(progressPercent);\n }\n }\n}\n\nexport async function preprocessToCar(\n input: File | ReadableStream<Uint8Array> | File[],\n options?: CarPreprocessOptions,\n): Promise<CarPreprocessResult> {\n let files: File[];\n\n if (input instanceof ReadableStream) {\n const [streamForSize, streamForFile] = input.tee();\n const size = await calculateStreamSize(streamForSize, options?.signal);\n const streamBlob = await streamToBlob(\n streamForFile,\n \"application/octet-stream\",\n );\n files = [\n new File([streamBlob], options?.name || \"upload\", {\n type: streamBlob.type,\n }),\n ];\n } else if (Array.isArray(input)) {\n files = input;\n } else {\n files = [input];\n }\n\n // wrapWithDirectory must be true for directory uploads (File[] input) so the\n // last addAll yield is the root directory CID, not a child file/subdirectory.\n // Single files don't need wrapping. Matches Go SDK wrapInDir behavior.\n const wrapWithDirectory = Array.isArray(input);\n\n const heliaInstance = await getHelia();\n const fs = unixfs(heliaInstance);\n const c = car(heliaInstance);\n\n let rootCid: CID | undefined;\n let blocksCount = 0n;\n\n const src = fileSource(files, options?.onProgress, options?.signal);\n\n let hasFiles = false;\n for await (const result of fs.addAll(src, {\n cidVersion: 1,\n rawLeaves: false,\n wrapWithDirectory,\n signal: options?.signal,\n onProgress(event) {\n if (event.type === \"blocks:put:blockstore:put\") {\n blocksCount++;\n }\n },\n })) {\n if (options?.signal?.aborted) {\n throw new Error(\"Aborted\");\n }\n rootCid = result.cid;\n hasFiles = true;\n }\n\n if (!hasFiles || !rootCid) {\n throw new Error(\"No files to process\");\n }\n\n // c.export() now returns an async generator directly (was renamed from 'stream')\n const carAsyncGenerator = c.export(rootCid!, { signal: options?.signal });\n const carStream = asyncGeneratorToReadableStream(carAsyncGenerator);\n\n // Use stream tee to create two identical streams - one for size calculation, one for processing\n const [streamForSize, streamForProcessing] = carStream.tee();\n\n const size = await calculateStreamSize(streamForSize, options?.signal);\n\n return {\n carStream: streamForProcessing,\n rootCid: rootCid!.toString(),\n size,\n };\n}\n\nexport async function isCarFile(file: File): Promise<boolean> {\n if (file.type !== MIME_TYPE_CAR && !file.name.endsWith(FILE_EXTENSION_CAR)) {\n return false;\n }\n\n try {\n const iterable = readableStreamToAsyncIterable(file.stream());\n const reader = await CarReader.fromIterable(iterable);\n const roots = await reader.getRoots();\n return roots.length > 0;\n } catch {\n return false;\n }\n}\n\nexport async function destroyCarPreprocessor() {\n await cleanupHelia();\n}\n"],"mappings":"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"}
|
|
@@ -1,20 +1,46 @@
|
|
|
1
1
|
import { PinnerConfig } from "../config.js";
|
|
2
|
+
import { AuthManager } from "../auth/manager.js";
|
|
2
3
|
import { UploadInput, UploadOperation, UploadOptions, UploadResult } from "../types/upload.js";
|
|
3
4
|
import { OperationPollingOptions } from "@lumeweb/portal-sdk";
|
|
4
5
|
|
|
5
6
|
//#region src/upload/manager.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Handles file uploads via XHR or TUS protocol based on file size.
|
|
9
|
+
*/
|
|
6
10
|
declare class UploadManager {
|
|
7
11
|
#private;
|
|
8
12
|
private config;
|
|
13
|
+
private readonly auth;
|
|
9
14
|
private xhrHandler;
|
|
10
15
|
private tusHandler;
|
|
11
16
|
private portalSdk;
|
|
12
17
|
private uploadLimit;
|
|
13
18
|
private limitFetched;
|
|
14
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Create a new UploadManager.
|
|
21
|
+
* @param config SDK configuration
|
|
22
|
+
* @param auth AuthManager for authentication
|
|
23
|
+
*/
|
|
24
|
+
constructor(config: PinnerConfig, auth: AuthManager);
|
|
25
|
+
/**
|
|
26
|
+
* Fetch the upload size limit from the API. Falls back to 100 MB on failure.
|
|
27
|
+
*/
|
|
15
28
|
fetchUploadLimit(): Promise<number>;
|
|
29
|
+
/**
|
|
30
|
+
* Get the cached upload size limit (in bytes). Call fetchUploadLimit() first.
|
|
31
|
+
*/
|
|
16
32
|
getUploadLimit(): number;
|
|
33
|
+
/**
|
|
34
|
+
* Upload a file or stream to IPFS.
|
|
35
|
+
* @param input File, stream, or upload object
|
|
36
|
+
* @param options Upload configuration
|
|
37
|
+
*/
|
|
17
38
|
upload(input: UploadInput, options?: UploadOptions): Promise<UploadOperation>;
|
|
39
|
+
/**
|
|
40
|
+
* Upload a CAR file or stream directly without preprocessing.
|
|
41
|
+
* @param input CAR file or stream
|
|
42
|
+
* @param options Upload configuration
|
|
43
|
+
*/
|
|
18
44
|
uploadCar(input: File | ReadableStream<Uint8Array>, options?: UploadOptions): Promise<UploadOperation>;
|
|
19
45
|
/**
|
|
20
46
|
* Wait for an operation to complete or reach a settled state.
|
|
@@ -28,6 +54,11 @@ declare class UploadManager {
|
|
|
28
54
|
* @returns UploadResult with operation status merged in
|
|
29
55
|
*/
|
|
30
56
|
waitForOperation(input: number | UploadResult, options?: OperationPollingOptions): Promise<UploadResult>;
|
|
57
|
+
/**
|
|
58
|
+
* Upload an array of files as an IPFS directory.
|
|
59
|
+
* @param files Files to upload
|
|
60
|
+
* @param options Upload configuration
|
|
61
|
+
*/
|
|
31
62
|
uploadDirectory(files: File[], options?: UploadOptions): Promise<UploadOperation>;
|
|
32
63
|
destroy(): void;
|
|
33
64
|
}
|
|
@@ -10,23 +10,37 @@ import { AccountError, OPERATION_STATUS, Sdk, poll } from "@lumeweb/portal-sdk";
|
|
|
10
10
|
import { createEqFilter } from "@lumeweb/query-builder";
|
|
11
11
|
|
|
12
12
|
//#region src/upload/manager.ts
|
|
13
|
+
/**
|
|
14
|
+
* Handles file uploads via XHR or TUS protocol based on file size.
|
|
15
|
+
*/
|
|
13
16
|
var UploadManager = class {
|
|
14
17
|
config;
|
|
18
|
+
auth;
|
|
15
19
|
xhrHandler;
|
|
16
20
|
tusHandler;
|
|
17
21
|
portalSdk;
|
|
18
22
|
uploadLimit = TUS_SIZE_THRESHOLD;
|
|
19
23
|
limitFetched = false;
|
|
20
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Create a new UploadManager.
|
|
26
|
+
* @param config SDK configuration
|
|
27
|
+
* @param auth AuthManager for authentication
|
|
28
|
+
*/
|
|
29
|
+
constructor(config, auth) {
|
|
21
30
|
this.config = config;
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
31
|
+
this.auth = auth;
|
|
32
|
+
this.xhrHandler = new XHRUploadHandler(config, auth);
|
|
33
|
+
this.tusHandler = new TUSUploadHandler(config, auth);
|
|
24
34
|
this.portalSdk = new Sdk(config.endpoint || "https://ipfs.pinner.xyz");
|
|
35
|
+
this.portalSdk.setAuthToken(auth.getAuthToken());
|
|
25
36
|
configureCar({
|
|
26
37
|
datastoreName: config.datastoreName,
|
|
27
38
|
datastore: config.datastore
|
|
28
39
|
});
|
|
29
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Fetch the upload size limit from the API. Falls back to 100 MB on failure.
|
|
43
|
+
*/
|
|
30
44
|
async fetchUploadLimit() {
|
|
31
45
|
if (this.limitFetched) return this.uploadLimit;
|
|
32
46
|
try {
|
|
@@ -38,13 +52,26 @@ var UploadManager = class {
|
|
|
38
52
|
this.limitFetched = true;
|
|
39
53
|
return this.uploadLimit;
|
|
40
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Get the cached upload size limit (in bytes). Call fetchUploadLimit() first.
|
|
57
|
+
*/
|
|
41
58
|
getUploadLimit() {
|
|
42
59
|
return this.uploadLimit;
|
|
43
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Upload a file or stream to IPFS.
|
|
63
|
+
* @param input File, stream, or upload object
|
|
64
|
+
* @param options Upload configuration
|
|
65
|
+
*/
|
|
44
66
|
async upload(input, options) {
|
|
45
67
|
this.#validateInput(input, options);
|
|
46
68
|
return this.#uploadInput(input, options);
|
|
47
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Upload a CAR file or stream directly without preprocessing.
|
|
72
|
+
* @param input CAR file or stream
|
|
73
|
+
* @param options Upload configuration
|
|
74
|
+
*/
|
|
48
75
|
async uploadCar(input, options) {
|
|
49
76
|
this.#validateInput(input, options);
|
|
50
77
|
return this.#uploadCarFile(input, options);
|
|
@@ -140,7 +167,7 @@ var UploadManager = class {
|
|
|
140
167
|
const uploadId = uploadResult.id;
|
|
141
168
|
if (!uploadId) throw new Error("No upload ID available to poll for upload result");
|
|
142
169
|
const fetchUrl = `${this.config.endpoint || "https://ipfs.pinner.xyz"}/api/upload/result/${encodeURIComponent(uploadId)}`;
|
|
143
|
-
const headers =
|
|
170
|
+
const headers = this.auth.getAuthHeaders();
|
|
144
171
|
const result = await poll(async () => {
|
|
145
172
|
const response = await fetch(fetchUrl, { headers });
|
|
146
173
|
if (response.status === 404) return {
|
|
@@ -173,6 +200,11 @@ var UploadManager = class {
|
|
|
173
200
|
if (options?.size !== void 0 && options.size === 0) throw new EmptyFileError("Cannot upload empty stream");
|
|
174
201
|
}
|
|
175
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* Upload an array of files as an IPFS directory.
|
|
205
|
+
* @param files Files to upload
|
|
206
|
+
* @param options Upload configuration
|
|
207
|
+
*/
|
|
176
208
|
async uploadDirectory(files, options) {
|
|
177
209
|
const carResult = await preprocessToCar(files, {
|
|
178
210
|
onProgress: options?.onProgress ? (p) => options.onProgress({
|