@opendatalabs/vana-sdk 0.1.0-alpha.a25bcc7 → 0.1.0-alpha.a6b60fc
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/browser.cjs.map +1 -1
- package/dist/browser.d.ts +33 -1
- package/dist/browser.js.map +1 -1
- package/dist/chains/index.cjs.map +1 -1
- package/dist/chains/index.d.ts +30 -1
- package/dist/chains/index.js.map +1 -1
- package/dist/config/chains.cjs.map +1 -1
- package/dist/config/chains.d.ts +99 -0
- package/dist/config/chains.js.map +1 -1
- package/dist/contracts/contractController.cjs.map +1 -1
- package/dist/contracts/contractController.d.ts +66 -10
- package/dist/contracts/contractController.js.map +1 -1
- package/dist/controllers/data.cjs +172 -140
- package/dist/controllers/data.cjs.map +1 -1
- package/dist/controllers/data.d.ts +213 -175
- package/dist/controllers/data.js +172 -140
- package/dist/controllers/data.js.map +1 -1
- package/dist/controllers/permissions.cjs +184 -190
- package/dist/controllers/permissions.cjs.map +1 -1
- package/dist/controllers/permissions.d.ts +29 -73
- package/dist/controllers/permissions.js +184 -190
- package/dist/controllers/permissions.js.map +1 -1
- package/dist/controllers/protocol.cjs.map +1 -1
- package/dist/controllers/protocol.d.ts +27 -28
- package/dist/controllers/protocol.js.map +1 -1
- package/dist/controllers/schemas.cjs +19 -17
- package/dist/controllers/schemas.cjs.map +1 -1
- package/dist/controllers/schemas.d.ts +47 -40
- package/dist/controllers/schemas.js +19 -17
- package/dist/controllers/schemas.js.map +1 -1
- package/dist/controllers/server.cjs +17 -15
- package/dist/controllers/server.cjs.map +1 -1
- package/dist/controllers/server.d.ts +46 -38
- package/dist/controllers/server.js +17 -15
- package/dist/controllers/server.js.map +1 -1
- package/dist/core/apiClient.cjs +53 -3
- package/dist/core/apiClient.cjs.map +1 -1
- package/dist/core/apiClient.d.ts +132 -7
- package/dist/core/apiClient.js +53 -3
- package/dist/core/apiClient.js.map +1 -1
- package/dist/core/generics.cjs +30 -3
- package/dist/core/generics.cjs.map +1 -1
- package/dist/core/generics.d.ts +95 -6
- package/dist/core/generics.js +30 -3
- package/dist/core/generics.js.map +1 -1
- package/dist/core.cjs +29 -12
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.ts +2 -1
- package/dist/core.js +29 -12
- package/dist/core.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.node.cjs +3 -3
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.d.ts +8 -9
- package/dist/index.node.js +2 -2
- package/dist/index.node.js.map +1 -1
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.ts +39 -1
- package/dist/node.js.map +1 -1
- package/dist/platform/browser.cjs +160 -2
- package/dist/platform/browser.cjs.map +1 -1
- package/dist/platform/browser.d.ts +232 -12
- package/dist/platform/browser.js +160 -2
- package/dist/platform/browser.js.map +1 -1
- package/dist/platform/interface.cjs.map +1 -1
- package/dist/platform/interface.d.ts +283 -90
- package/dist/platform/node.cjs +163 -2
- package/dist/platform/node.cjs.map +1 -1
- package/dist/platform/node.d.ts +69 -6
- package/dist/platform/node.js +163 -2
- package/dist/platform/node.js.map +1 -1
- package/dist/server/relayerHandler.cjs +214 -0
- package/dist/server/relayerHandler.cjs.map +1 -0
- package/dist/server/relayerHandler.d.ts +36 -0
- package/dist/server/relayerHandler.js +190 -0
- package/dist/server/relayerHandler.js.map +1 -0
- package/dist/storage/manager.cjs +108 -25
- package/dist/storage/manager.cjs.map +1 -1
- package/dist/storage/manager.d.ts +119 -25
- package/dist/storage/manager.js +108 -25
- package/dist/storage/manager.js.map +1 -1
- package/dist/storage/providers/callback-storage.cjs +86 -15
- package/dist/storage/providers/callback-storage.cjs.map +1 -1
- package/dist/storage/providers/callback-storage.d.ts +109 -20
- package/dist/storage/providers/callback-storage.js +86 -15
- package/dist/storage/providers/callback-storage.js.map +1 -1
- package/dist/storage/providers/pinata.cjs.map +1 -1
- package/dist/storage/providers/pinata.d.ts +12 -14
- package/dist/storage/providers/pinata.js.map +1 -1
- package/dist/tests/factories/mockFactory.d.ts +2 -2
- package/dist/tests/relayer-integration.test.d.ts +1 -0
- package/dist/tests/relayer-unified.test.d.ts +1 -0
- package/dist/tests/server-relayer-handler.test.d.ts +1 -0
- package/dist/types/blockchain.cjs.map +1 -1
- package/dist/types/blockchain.d.ts +39 -11
- package/dist/types/chains.cjs.map +1 -1
- package/dist/types/chains.d.ts +74 -7
- package/dist/types/chains.js.map +1 -1
- package/dist/types/config.cjs.map +1 -1
- package/dist/types/config.d.ts +46 -191
- package/dist/types/config.js.map +1 -1
- package/dist/types/contracts.cjs.map +1 -1
- package/dist/types/contracts.d.ts +71 -7
- package/dist/types/controller-context.cjs.map +1 -1
- package/dist/types/controller-context.d.ts +3 -2
- package/dist/types/data.cjs.map +1 -1
- package/dist/types/data.d.ts +4 -6
- package/dist/types/generics.cjs.map +1 -1
- package/dist/types/generics.d.ts +80 -9
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.d.ts +27 -2
- package/dist/types/index.js.map +1 -1
- package/dist/types/operations.cjs.map +1 -1
- package/dist/types/operations.d.ts +132 -15
- package/dist/types/operations.js.map +1 -1
- package/dist/types/permissions.cjs.map +1 -1
- package/dist/types/permissions.d.ts +15 -20
- package/dist/types/personal.cjs.map +1 -1
- package/dist/types/personal.d.ts +131 -14
- package/dist/types/relayer.cjs.map +1 -1
- package/dist/types/relayer.d.ts +262 -35
- package/dist/types/storage.cjs.map +1 -1
- package/dist/types/storage.d.ts +9 -21
- package/dist/types/storage.js.map +1 -1
- package/dist/utils/grantFiles.cjs.map +1 -1
- package/dist/utils/grantFiles.d.ts +10 -20
- package/dist/utils/grantFiles.js.map +1 -1
- package/dist/utils/grantValidation.cjs.map +1 -1
- package/dist/utils/grantValidation.d.ts +95 -16
- package/dist/utils/grantValidation.js.map +1 -1
- package/dist/utils/grants.cjs.map +1 -1
- package/dist/utils/grants.d.ts +93 -12
- package/dist/utils/grants.js.map +1 -1
- package/dist/utils/lazy-import.cjs.map +1 -1
- package/dist/utils/lazy-import.d.ts +32 -7
- package/dist/utils/lazy-import.js.map +1 -1
- package/dist/utils/signatureCache.cjs +8 -2
- package/dist/utils/signatureCache.cjs.map +1 -1
- package/dist/utils/signatureCache.d.ts +49 -8
- package/dist/utils/signatureCache.js +8 -2
- package/dist/utils/signatureCache.js.map +1 -1
- package/dist/utils/transactionHelpers.cjs.map +1 -1
- package/dist/utils/transactionHelpers.d.ts +12 -12
- package/dist/utils/transactionHelpers.js.map +1 -1
- package/dist/utils/typedDataConverter.cjs.map +1 -1
- package/dist/utils/typedDataConverter.d.ts +39 -3
- package/dist/utils/typedDataConverter.js.map +1 -1
- package/dist/utils/urlResolver.cjs +7 -0
- package/dist/utils/urlResolver.cjs.map +1 -1
- package/dist/utils/urlResolver.d.ts +22 -4
- package/dist/utils/urlResolver.js +7 -0
- package/dist/utils/urlResolver.js.map +1 -1
- package/dist/utils/wallet.cjs +2 -1
- package/dist/utils/wallet.cjs.map +1 -1
- package/dist/utils/wallet.d.ts +78 -16
- package/dist/utils/wallet.js +2 -1
- package/dist/utils/wallet.js.map +1 -1
- package/package.json +1 -1
- package/dist/server/handler.cjs +0 -101
- package/dist/server/handler.cjs.map +0 -1
- package/dist/server/handler.d.ts +0 -87
- package/dist/server/handler.js +0 -77
- package/dist/server/handler.js.map +0 -1
- /package/dist/tests/{server-handler.test.d.ts → permissions-revoke-relayer.test.d.ts} +0 -0
|
@@ -1,13 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provides user-defined storage operations through callback functions.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* This module implements a flexible storage provider that delegates all
|
|
6
|
+
* operations to user-provided callbacks. It enables custom storage
|
|
7
|
+
* integrations without modifying the SDK, supporting any backend including
|
|
8
|
+
* HTTP APIs, WebSocket servers, cloud storage services, or local filesystems.
|
|
9
|
+
*
|
|
10
|
+
* @category Storage
|
|
11
|
+
* @module storage/providers/callback-storage
|
|
12
|
+
*/
|
|
1
13
|
import type { StorageCallbacks } from "../../types/config";
|
|
2
14
|
import { type StorageProvider, type StorageUploadResult, type StorageFile, type StorageListOptions, type StorageProviderConfig } from "../../types/storage";
|
|
3
15
|
/**
|
|
4
|
-
*
|
|
16
|
+
* Delegates storage operations to user-provided callback functions.
|
|
5
17
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
18
|
+
* @remarks
|
|
19
|
+
* This provider enables custom storage integrations by delegating all
|
|
20
|
+
* operations to user-defined callbacks. It follows the same flexible
|
|
21
|
+
* pattern as relayer callbacks, allowing implementations via HTTP,
|
|
22
|
+
* WebSocket, direct cloud APIs, local filesystem, or any other backend.
|
|
23
|
+
*
|
|
24
|
+
* The provider validates callback results and wraps errors in consistent
|
|
25
|
+
* `StorageError` types for uniform error handling across the SDK.
|
|
9
26
|
*
|
|
10
|
-
* @category Storage
|
|
11
27
|
* @example
|
|
12
28
|
* ```typescript
|
|
13
29
|
* // HTTP-based implementation
|
|
@@ -52,43 +68,116 @@ import { type StorageProvider, type StorageUploadResult, type StorageFile, type
|
|
|
52
68
|
* }
|
|
53
69
|
* };
|
|
54
70
|
* ```
|
|
71
|
+
*
|
|
72
|
+
* @category Storage
|
|
55
73
|
*/
|
|
56
74
|
export declare class CallbackStorage implements StorageProvider {
|
|
57
75
|
private readonly callbacks;
|
|
76
|
+
/**
|
|
77
|
+
* Creates a new callback-based storage provider.
|
|
78
|
+
*
|
|
79
|
+
* @param callbacks - User-provided storage operation callbacks.
|
|
80
|
+
* Must include at minimum `upload` and `download` functions.
|
|
81
|
+
* @throws {Error} If required callbacks are missing
|
|
82
|
+
*/
|
|
58
83
|
constructor(callbacks: StorageCallbacks);
|
|
59
84
|
/**
|
|
60
|
-
*
|
|
85
|
+
* Uploads a file using the user-provided callback.
|
|
61
86
|
*
|
|
62
|
-
* @param file - The blob to upload
|
|
63
|
-
*
|
|
64
|
-
* @
|
|
87
|
+
* @param file - The blob to upload.
|
|
88
|
+
* Can be any Blob-compatible object including File.
|
|
89
|
+
* @param filename - Optional filename for the upload.
|
|
90
|
+
* If not provided, callback may generate a name.
|
|
91
|
+
* @returns Upload result containing URL and metadata
|
|
92
|
+
*
|
|
93
|
+
* @throws {StorageError} With code 'INVALID_UPLOAD_RESULT' if callback returns invalid data
|
|
94
|
+
* @throws {StorageError} With code 'UPLOAD_ERROR' if upload fails
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* const file = new File(['content'], 'data.json');
|
|
99
|
+
* const result = await storage.upload(file);
|
|
100
|
+
* console.log('Uploaded to:', result.url);
|
|
101
|
+
* ```
|
|
65
102
|
*/
|
|
66
103
|
upload(file: Blob, filename?: string): Promise<StorageUploadResult>;
|
|
67
104
|
/**
|
|
68
|
-
*
|
|
105
|
+
* Downloads a file using the user-provided callback.
|
|
106
|
+
*
|
|
107
|
+
* @param url - The URL or identifier to download.
|
|
108
|
+
* If `extractIdentifier` callback is provided, it will be used to extract the identifier.
|
|
109
|
+
* @returns The downloaded file as a Blob
|
|
69
110
|
*
|
|
70
|
-
* @
|
|
71
|
-
* @
|
|
111
|
+
* @throws {StorageError} With code 'INVALID_DOWNLOAD_RESULT' if callback returns non-Blob
|
|
112
|
+
* @throws {StorageError} With code 'DOWNLOAD_ERROR' if download fails
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```typescript
|
|
116
|
+
* const blob = await storage.download('https://storage.example.com/file123');
|
|
117
|
+
* const text = await blob.text();
|
|
118
|
+
* ```
|
|
72
119
|
*/
|
|
73
120
|
download(url: string): Promise<Blob>;
|
|
74
121
|
/**
|
|
75
|
-
*
|
|
122
|
+
* Lists files using the user-provided callback.
|
|
123
|
+
*
|
|
124
|
+
* @param options - Optional list options.
|
|
125
|
+
* @param options.namePattern - Pattern to filter files by name.
|
|
126
|
+
* Implementation depends on callback.
|
|
127
|
+
* @param options.limit - Maximum number of files to return.
|
|
128
|
+
* Implementation depends on callback.
|
|
129
|
+
* @returns Array of storage file metadata
|
|
130
|
+
*
|
|
131
|
+
* @throws {StorageError} With code 'NOT_SUPPORTED' if list callback not provided
|
|
132
|
+
* @throws {StorageError} With code 'LIST_ERROR' if listing fails
|
|
76
133
|
*
|
|
77
|
-
* @
|
|
78
|
-
*
|
|
134
|
+
* @remarks
|
|
135
|
+
* This operation is optional and only available if a `list` callback
|
|
136
|
+
* is provided during construction.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```typescript
|
|
140
|
+
* const files = await storage.list({ namePattern: '*.json' });
|
|
141
|
+
* files.forEach(file => console.log(file.name, file.size));
|
|
142
|
+
* ```
|
|
79
143
|
*/
|
|
80
144
|
list(options?: StorageListOptions): Promise<StorageFile[]>;
|
|
81
145
|
/**
|
|
82
|
-
*
|
|
146
|
+
* Deletes a file using the user-provided callback.
|
|
147
|
+
*
|
|
148
|
+
* @param url - The URL or identifier to delete.
|
|
149
|
+
* If `extractIdentifier` callback is provided, it will be used to extract the identifier.
|
|
150
|
+
* @returns True if deletion succeeded, false otherwise
|
|
151
|
+
*
|
|
152
|
+
* @throws {StorageError} With code 'NOT_SUPPORTED' if delete callback not provided
|
|
153
|
+
* @throws {StorageError} With code 'DELETE_ERROR' if deletion fails
|
|
83
154
|
*
|
|
84
|
-
* @
|
|
85
|
-
*
|
|
155
|
+
* @remarks
|
|
156
|
+
* This operation is optional and only available if a `delete` callback
|
|
157
|
+
* is provided during construction.
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```typescript
|
|
161
|
+
* const deleted = await storage.delete('https://storage.example.com/file123');
|
|
162
|
+
* if (deleted) {
|
|
163
|
+
* console.log('File deleted successfully');
|
|
164
|
+
* }
|
|
165
|
+
* ```
|
|
86
166
|
*/
|
|
87
167
|
delete(url: string): Promise<boolean>;
|
|
88
168
|
/**
|
|
89
|
-
*
|
|
169
|
+
* Returns the provider's configuration and capabilities.
|
|
170
|
+
*
|
|
171
|
+
* @returns Configuration object indicating supported features
|
|
90
172
|
*
|
|
91
|
-
* @
|
|
173
|
+
* @example
|
|
174
|
+
* ```typescript
|
|
175
|
+
* const config = storage.getConfig();
|
|
176
|
+
* if (config.features.list) {
|
|
177
|
+
* // List operation is supported
|
|
178
|
+
* const files = await storage.list();
|
|
179
|
+
* }
|
|
180
|
+
* ```
|
|
92
181
|
*/
|
|
93
182
|
getConfig(): StorageProviderConfig;
|
|
94
183
|
}
|
|
@@ -2,6 +2,13 @@ import {
|
|
|
2
2
|
StorageError
|
|
3
3
|
} from "../../types/storage";
|
|
4
4
|
class CallbackStorage {
|
|
5
|
+
/**
|
|
6
|
+
* Creates a new callback-based storage provider.
|
|
7
|
+
*
|
|
8
|
+
* @param callbacks - User-provided storage operation callbacks.
|
|
9
|
+
* Must include at minimum `upload` and `download` functions.
|
|
10
|
+
* @throws {Error} If required callbacks are missing
|
|
11
|
+
*/
|
|
5
12
|
constructor(callbacks) {
|
|
6
13
|
this.callbacks = callbacks;
|
|
7
14
|
if (!callbacks.upload || !callbacks.download) {
|
|
@@ -11,11 +18,23 @@ class CallbackStorage {
|
|
|
11
18
|
}
|
|
12
19
|
}
|
|
13
20
|
/**
|
|
14
|
-
*
|
|
21
|
+
* Uploads a file using the user-provided callback.
|
|
22
|
+
*
|
|
23
|
+
* @param file - The blob to upload.
|
|
24
|
+
* Can be any Blob-compatible object including File.
|
|
25
|
+
* @param filename - Optional filename for the upload.
|
|
26
|
+
* If not provided, callback may generate a name.
|
|
27
|
+
* @returns Upload result containing URL and metadata
|
|
15
28
|
*
|
|
16
|
-
* @
|
|
17
|
-
* @
|
|
18
|
-
*
|
|
29
|
+
* @throws {StorageError} With code 'INVALID_UPLOAD_RESULT' if callback returns invalid data
|
|
30
|
+
* @throws {StorageError} With code 'UPLOAD_ERROR' if upload fails
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const file = new File(['content'], 'data.json');
|
|
35
|
+
* const result = await storage.upload(file);
|
|
36
|
+
* console.log('Uploaded to:', result.url);
|
|
37
|
+
* ```
|
|
19
38
|
*/
|
|
20
39
|
async upload(file, filename) {
|
|
21
40
|
try {
|
|
@@ -41,10 +60,20 @@ class CallbackStorage {
|
|
|
41
60
|
}
|
|
42
61
|
}
|
|
43
62
|
/**
|
|
44
|
-
*
|
|
63
|
+
* Downloads a file using the user-provided callback.
|
|
64
|
+
*
|
|
65
|
+
* @param url - The URL or identifier to download.
|
|
66
|
+
* If `extractIdentifier` callback is provided, it will be used to extract the identifier.
|
|
67
|
+
* @returns The downloaded file as a Blob
|
|
68
|
+
*
|
|
69
|
+
* @throws {StorageError} With code 'INVALID_DOWNLOAD_RESULT' if callback returns non-Blob
|
|
70
|
+
* @throws {StorageError} With code 'DOWNLOAD_ERROR' if download fails
|
|
45
71
|
*
|
|
46
|
-
* @
|
|
47
|
-
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* const blob = await storage.download('https://storage.example.com/file123');
|
|
75
|
+
* const text = await blob.text();
|
|
76
|
+
* ```
|
|
48
77
|
*/
|
|
49
78
|
async download(url) {
|
|
50
79
|
try {
|
|
@@ -71,10 +100,27 @@ class CallbackStorage {
|
|
|
71
100
|
}
|
|
72
101
|
}
|
|
73
102
|
/**
|
|
74
|
-
*
|
|
103
|
+
* Lists files using the user-provided callback.
|
|
75
104
|
*
|
|
76
|
-
* @param options - Optional list options
|
|
77
|
-
* @
|
|
105
|
+
* @param options - Optional list options.
|
|
106
|
+
* @param options.namePattern - Pattern to filter files by name.
|
|
107
|
+
* Implementation depends on callback.
|
|
108
|
+
* @param options.limit - Maximum number of files to return.
|
|
109
|
+
* Implementation depends on callback.
|
|
110
|
+
* @returns Array of storage file metadata
|
|
111
|
+
*
|
|
112
|
+
* @throws {StorageError} With code 'NOT_SUPPORTED' if list callback not provided
|
|
113
|
+
* @throws {StorageError} With code 'LIST_ERROR' if listing fails
|
|
114
|
+
*
|
|
115
|
+
* @remarks
|
|
116
|
+
* This operation is optional and only available if a `list` callback
|
|
117
|
+
* is provided during construction.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```typescript
|
|
121
|
+
* const files = await storage.list({ namePattern: '*.json' });
|
|
122
|
+
* files.forEach(file => console.log(file.name, file.size));
|
|
123
|
+
* ```
|
|
78
124
|
*/
|
|
79
125
|
async list(options) {
|
|
80
126
|
if (!this.callbacks.list) {
|
|
@@ -105,10 +151,26 @@ class CallbackStorage {
|
|
|
105
151
|
}
|
|
106
152
|
}
|
|
107
153
|
/**
|
|
108
|
-
*
|
|
154
|
+
* Deletes a file using the user-provided callback.
|
|
155
|
+
*
|
|
156
|
+
* @param url - The URL or identifier to delete.
|
|
157
|
+
* If `extractIdentifier` callback is provided, it will be used to extract the identifier.
|
|
158
|
+
* @returns True if deletion succeeded, false otherwise
|
|
109
159
|
*
|
|
110
|
-
* @
|
|
111
|
-
* @
|
|
160
|
+
* @throws {StorageError} With code 'NOT_SUPPORTED' if delete callback not provided
|
|
161
|
+
* @throws {StorageError} With code 'DELETE_ERROR' if deletion fails
|
|
162
|
+
*
|
|
163
|
+
* @remarks
|
|
164
|
+
* This operation is optional and only available if a `delete` callback
|
|
165
|
+
* is provided during construction.
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* const deleted = await storage.delete('https://storage.example.com/file123');
|
|
170
|
+
* if (deleted) {
|
|
171
|
+
* console.log('File deleted successfully');
|
|
172
|
+
* }
|
|
173
|
+
* ```
|
|
112
174
|
*/
|
|
113
175
|
async delete(url) {
|
|
114
176
|
if (!this.callbacks.delete) {
|
|
@@ -131,9 +193,18 @@ class CallbackStorage {
|
|
|
131
193
|
}
|
|
132
194
|
}
|
|
133
195
|
/**
|
|
134
|
-
*
|
|
196
|
+
* Returns the provider's configuration and capabilities.
|
|
197
|
+
*
|
|
198
|
+
* @returns Configuration object indicating supported features
|
|
135
199
|
*
|
|
136
|
-
* @
|
|
200
|
+
* @example
|
|
201
|
+
* ```typescript
|
|
202
|
+
* const config = storage.getConfig();
|
|
203
|
+
* if (config.features.list) {
|
|
204
|
+
* // List operation is supported
|
|
205
|
+
* const files = await storage.list();
|
|
206
|
+
* }
|
|
207
|
+
* ```
|
|
137
208
|
*/
|
|
138
209
|
getConfig() {
|
|
139
210
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/storage/providers/callback-storage.ts"],"sourcesContent":["import type { StorageCallbacks } from \"../../types/config\";\nimport {\n StorageError,\n type StorageProvider,\n type StorageUploadResult,\n type StorageFile,\n type StorageListOptions,\n type StorageProviderConfig,\n} from \"../../types/storage\";\n\n/**\n * Storage provider that delegates all operations to user-provided callbacks.\n *\n * This provider follows the same flexible pattern as relayer callbacks,\n * allowing users to implement storage operations in any way they choose\n * (HTTP, WebSocket, direct cloud APIs, local filesystem, etc.).\n *\n * @category Storage\n * @example\n * ```typescript\n * // HTTP-based implementation\n * const httpCallbacks: StorageCallbacks = {\n * async upload(blob, filename) {\n * const formData = new FormData();\n * formData.append('file', blob, filename);\n * const response = await fetch('/api/storage/upload', {\n * method: 'POST',\n * body: formData\n * });\n * const data = await response.json();\n * return {\n * url: data.url,\n * size: blob.size,\n * contentType: blob.type\n * };\n * },\n * async download(identifier) {\n * const response = await fetch(`/api/storage/download/${identifier}`);\n * return response.blob();\n * }\n * };\n *\n * const storage = new CallbackStorage(httpCallbacks);\n *\n * // Direct S3 implementation\n * const s3Callbacks: StorageCallbacks = {\n * async upload(blob, filename) {\n * const url = await getPresignedUploadUrl(filename);\n * await fetch(url, { method: 'PUT', body: blob });\n * return {\n * url: `s3://my-bucket/${filename}`,\n * size: blob.size,\n * contentType: blob.type\n * };\n * },\n * async download(identifier) {\n * const url = await getPresignedDownloadUrl(identifier);\n * const response = await fetch(url);\n * return response.blob();\n * }\n * };\n * ```\n */\nexport class CallbackStorage implements StorageProvider {\n constructor(private readonly callbacks: StorageCallbacks) {\n if (!callbacks.upload || !callbacks.download) {\n throw new Error(\n \"CallbackStorage requires both upload and download callbacks\",\n );\n }\n }\n\n /**\n * Upload a file using the provided callback\n *\n * @param file - The blob to upload\n * @param filename - Optional filename for the upload\n * @returns The upload result with URL and metadata\n */\n async upload(file: Blob, filename?: string): Promise<StorageUploadResult> {\n try {\n const result = await this.callbacks.upload(file, filename);\n\n // Validate the result has required fields\n if (!result.url || result.url.trim() === \"\") {\n throw new StorageError(\n \"Upload callback returned invalid result: missing or empty url\",\n \"INVALID_UPLOAD_RESULT\",\n \"callback-storage\",\n );\n }\n\n return result;\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Upload failed: ${error instanceof Error ? error.message : String(error)}`,\n \"UPLOAD_ERROR\",\n \"callback-storage\",\n { cause: error instanceof Error ? error : undefined },\n );\n }\n }\n\n /**\n * Download a file using the provided callback\n *\n * @param url - The URL or identifier to download\n * @returns The downloaded blob\n */\n async download(url: string): Promise<Blob> {\n try {\n // Extract identifier if callback is provided, otherwise use URL as-is\n const identifier = this.callbacks.extractIdentifier\n ? this.callbacks.extractIdentifier(url)\n : url;\n\n const blob = await this.callbacks.download(identifier);\n\n if (!(blob instanceof Blob)) {\n throw new StorageError(\n \"Download callback returned invalid result: expected Blob\",\n \"INVALID_DOWNLOAD_RESULT\",\n \"callback-storage\",\n );\n }\n\n return blob;\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Download failed: ${error instanceof Error ? error.message : String(error)}`,\n \"DOWNLOAD_ERROR\",\n \"callback-storage\",\n { cause: error instanceof Error ? error : undefined },\n );\n }\n }\n\n /**\n * List files using the provided callback (if available)\n *\n * @param options - Optional list options including filters and pagination\n * @returns Array of storage files\n */\n async list(options?: StorageListOptions): Promise<StorageFile[]> {\n if (!this.callbacks.list) {\n throw new StorageError(\n \"List operation not supported - no list callback provided\",\n \"NOT_SUPPORTED\",\n \"callback-storage\",\n );\n }\n\n try {\n const result = await this.callbacks.list(options?.namePattern, options);\n\n // Convert list result to StorageFile format\n return result.items.map((item, index) => ({\n id: item.identifier,\n name: item.identifier.split(\"/\").pop() ?? `file-${index}`,\n url: item.identifier,\n size: item.size ?? 0,\n contentType: \"application/octet-stream\",\n createdAt: item.lastModified ?? new Date(),\n metadata: item.metadata,\n }));\n } catch (error) {\n throw new StorageError(\n `List failed: ${error instanceof Error ? error.message : String(error)}`,\n \"LIST_ERROR\",\n \"callback-storage\",\n { cause: error instanceof Error ? error : undefined },\n );\n }\n }\n\n /**\n * Delete a file using the provided callback (if available)\n *\n * @param url - The URL or identifier to delete\n * @returns True if deletion succeeded\n */\n async delete(url: string): Promise<boolean> {\n if (!this.callbacks.delete) {\n throw new StorageError(\n \"Delete operation not supported - no delete callback provided\",\n \"NOT_SUPPORTED\",\n \"callback-storage\",\n );\n }\n\n try {\n // Extract identifier if callback is provided, otherwise use URL as-is\n const identifier = this.callbacks.extractIdentifier\n ? this.callbacks.extractIdentifier(url)\n : url;\n\n return await this.callbacks.delete(identifier);\n } catch (error) {\n throw new StorageError(\n `Delete failed: ${error instanceof Error ? error.message : String(error)}`,\n \"DELETE_ERROR\",\n \"callback-storage\",\n { cause: error instanceof Error ? error : undefined },\n );\n }\n }\n\n /**\n * Get provider configuration\n *\n * @returns Provider configuration metadata\n */\n getConfig(): StorageProviderConfig {\n return {\n name: \"callback-storage\",\n type: \"callback\",\n requiresAuth: false,\n features: {\n upload: true,\n download: true,\n list: !!this.callbacks.list,\n delete: !!this.callbacks.delete,\n },\n };\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/storage/providers/callback-storage.ts"],"sourcesContent":["/**\n * Provides user-defined storage operations through callback functions.\n *\n * @remarks\n * This module implements a flexible storage provider that delegates all\n * operations to user-provided callbacks. It enables custom storage\n * integrations without modifying the SDK, supporting any backend including\n * HTTP APIs, WebSocket servers, cloud storage services, or local filesystems.\n *\n * @category Storage\n * @module storage/providers/callback-storage\n */\n\nimport type { StorageCallbacks } from \"../../types/config\";\nimport {\n StorageError,\n type StorageProvider,\n type StorageUploadResult,\n type StorageFile,\n type StorageListOptions,\n type StorageProviderConfig,\n} from \"../../types/storage\";\n\n/**\n * Delegates storage operations to user-provided callback functions.\n *\n * @remarks\n * This provider enables custom storage integrations by delegating all\n * operations to user-defined callbacks. It follows the same flexible\n * pattern as relayer callbacks, allowing implementations via HTTP,\n * WebSocket, direct cloud APIs, local filesystem, or any other backend.\n *\n * The provider validates callback results and wraps errors in consistent\n * `StorageError` types for uniform error handling across the SDK.\n *\n * @example\n * ```typescript\n * // HTTP-based implementation\n * const httpCallbacks: StorageCallbacks = {\n * async upload(blob, filename) {\n * const formData = new FormData();\n * formData.append('file', blob, filename);\n * const response = await fetch('/api/storage/upload', {\n * method: 'POST',\n * body: formData\n * });\n * const data = await response.json();\n * return {\n * url: data.url,\n * size: blob.size,\n * contentType: blob.type\n * };\n * },\n * async download(identifier) {\n * const response = await fetch(`/api/storage/download/${identifier}`);\n * return response.blob();\n * }\n * };\n *\n * const storage = new CallbackStorage(httpCallbacks);\n *\n * // Direct S3 implementation\n * const s3Callbacks: StorageCallbacks = {\n * async upload(blob, filename) {\n * const url = await getPresignedUploadUrl(filename);\n * await fetch(url, { method: 'PUT', body: blob });\n * return {\n * url: `s3://my-bucket/${filename}`,\n * size: blob.size,\n * contentType: blob.type\n * };\n * },\n * async download(identifier) {\n * const url = await getPresignedDownloadUrl(identifier);\n * const response = await fetch(url);\n * return response.blob();\n * }\n * };\n * ```\n *\n * @category Storage\n */\nexport class CallbackStorage implements StorageProvider {\n /**\n * Creates a new callback-based storage provider.\n *\n * @param callbacks - User-provided storage operation callbacks.\n * Must include at minimum `upload` and `download` functions.\n * @throws {Error} If required callbacks are missing\n */\n constructor(private readonly callbacks: StorageCallbacks) {\n if (!callbacks.upload || !callbacks.download) {\n throw new Error(\n \"CallbackStorage requires both upload and download callbacks\",\n );\n }\n }\n\n /**\n * Uploads a file using the user-provided callback.\n *\n * @param file - The blob to upload.\n * Can be any Blob-compatible object including File.\n * @param filename - Optional filename for the upload.\n * If not provided, callback may generate a name.\n * @returns Upload result containing URL and metadata\n *\n * @throws {StorageError} With code 'INVALID_UPLOAD_RESULT' if callback returns invalid data\n * @throws {StorageError} With code 'UPLOAD_ERROR' if upload fails\n *\n * @example\n * ```typescript\n * const file = new File(['content'], 'data.json');\n * const result = await storage.upload(file);\n * console.log('Uploaded to:', result.url);\n * ```\n */\n async upload(file: Blob, filename?: string): Promise<StorageUploadResult> {\n try {\n const result = await this.callbacks.upload(file, filename);\n\n // Validate the result has required fields\n if (!result.url || result.url.trim() === \"\") {\n throw new StorageError(\n \"Upload callback returned invalid result: missing or empty url\",\n \"INVALID_UPLOAD_RESULT\",\n \"callback-storage\",\n );\n }\n\n return result;\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Upload failed: ${error instanceof Error ? error.message : String(error)}`,\n \"UPLOAD_ERROR\",\n \"callback-storage\",\n { cause: error instanceof Error ? error : undefined },\n );\n }\n }\n\n /**\n * Downloads a file using the user-provided callback.\n *\n * @param url - The URL or identifier to download.\n * If `extractIdentifier` callback is provided, it will be used to extract the identifier.\n * @returns The downloaded file as a Blob\n *\n * @throws {StorageError} With code 'INVALID_DOWNLOAD_RESULT' if callback returns non-Blob\n * @throws {StorageError} With code 'DOWNLOAD_ERROR' if download fails\n *\n * @example\n * ```typescript\n * const blob = await storage.download('https://storage.example.com/file123');\n * const text = await blob.text();\n * ```\n */\n async download(url: string): Promise<Blob> {\n try {\n // Extract identifier if callback is provided, otherwise use URL as-is\n const identifier = this.callbacks.extractIdentifier\n ? this.callbacks.extractIdentifier(url)\n : url;\n\n const blob = await this.callbacks.download(identifier);\n\n if (!(blob instanceof Blob)) {\n throw new StorageError(\n \"Download callback returned invalid result: expected Blob\",\n \"INVALID_DOWNLOAD_RESULT\",\n \"callback-storage\",\n );\n }\n\n return blob;\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Download failed: ${error instanceof Error ? error.message : String(error)}`,\n \"DOWNLOAD_ERROR\",\n \"callback-storage\",\n { cause: error instanceof Error ? error : undefined },\n );\n }\n }\n\n /**\n * Lists files using the user-provided callback.\n *\n * @param options - Optional list options.\n * @param options.namePattern - Pattern to filter files by name.\n * Implementation depends on callback.\n * @param options.limit - Maximum number of files to return.\n * Implementation depends on callback.\n * @returns Array of storage file metadata\n *\n * @throws {StorageError} With code 'NOT_SUPPORTED' if list callback not provided\n * @throws {StorageError} With code 'LIST_ERROR' if listing fails\n *\n * @remarks\n * This operation is optional and only available if a `list` callback\n * is provided during construction.\n *\n * @example\n * ```typescript\n * const files = await storage.list({ namePattern: '*.json' });\n * files.forEach(file => console.log(file.name, file.size));\n * ```\n */\n async list(options?: StorageListOptions): Promise<StorageFile[]> {\n if (!this.callbacks.list) {\n throw new StorageError(\n \"List operation not supported - no list callback provided\",\n \"NOT_SUPPORTED\",\n \"callback-storage\",\n );\n }\n\n try {\n const result = await this.callbacks.list(options?.namePattern, options);\n\n // Convert list result to StorageFile format\n return result.items.map((item, index) => ({\n id: item.identifier,\n name: item.identifier.split(\"/\").pop() ?? `file-${index}`,\n url: item.identifier,\n size: item.size ?? 0,\n contentType: \"application/octet-stream\",\n createdAt: item.lastModified ?? new Date(),\n metadata: item.metadata,\n }));\n } catch (error) {\n throw new StorageError(\n `List failed: ${error instanceof Error ? error.message : String(error)}`,\n \"LIST_ERROR\",\n \"callback-storage\",\n { cause: error instanceof Error ? error : undefined },\n );\n }\n }\n\n /**\n * Deletes a file using the user-provided callback.\n *\n * @param url - The URL or identifier to delete.\n * If `extractIdentifier` callback is provided, it will be used to extract the identifier.\n * @returns True if deletion succeeded, false otherwise\n *\n * @throws {StorageError} With code 'NOT_SUPPORTED' if delete callback not provided\n * @throws {StorageError} With code 'DELETE_ERROR' if deletion fails\n *\n * @remarks\n * This operation is optional and only available if a `delete` callback\n * is provided during construction.\n *\n * @example\n * ```typescript\n * const deleted = await storage.delete('https://storage.example.com/file123');\n * if (deleted) {\n * console.log('File deleted successfully');\n * }\n * ```\n */\n async delete(url: string): Promise<boolean> {\n if (!this.callbacks.delete) {\n throw new StorageError(\n \"Delete operation not supported - no delete callback provided\",\n \"NOT_SUPPORTED\",\n \"callback-storage\",\n );\n }\n\n try {\n // Extract identifier if callback is provided, otherwise use URL as-is\n const identifier = this.callbacks.extractIdentifier\n ? this.callbacks.extractIdentifier(url)\n : url;\n\n return await this.callbacks.delete(identifier);\n } catch (error) {\n throw new StorageError(\n `Delete failed: ${error instanceof Error ? error.message : String(error)}`,\n \"DELETE_ERROR\",\n \"callback-storage\",\n { cause: error instanceof Error ? error : undefined },\n );\n }\n }\n\n /**\n * Returns the provider's configuration and capabilities.\n *\n * @returns Configuration object indicating supported features\n *\n * @example\n * ```typescript\n * const config = storage.getConfig();\n * if (config.features.list) {\n * // List operation is supported\n * const files = await storage.list();\n * }\n * ```\n */\n getConfig(): StorageProviderConfig {\n return {\n name: \"callback-storage\",\n type: \"callback\",\n requiresAuth: false,\n features: {\n upload: true,\n download: true,\n list: !!this.callbacks.list,\n delete: !!this.callbacks.delete,\n },\n };\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/storage/providers/pinata.ts"],"sourcesContent":["import {\n StorageError,\n type StorageProvider,\n type StorageUploadResult,\n type StorageFile,\n type StorageListOptions,\n type StorageProviderConfig,\n} from \"../index\";\n\nexport interface PinataConfig {\n /** Pinata JWT token for authentication */\n jwt: string;\n /** Optional custom gateway URL (defaults to https://gateway.pinata.cloud) */\n gatewayUrl?: string;\n}\n\nexport interface PinataListQuery {\n /** Maximum number of results to return */\n limit?: number;\n /** Offset for pagination */\n offset?: number;\n /** Filter by name pattern */\n namePattern?: string;\n}\n\nexport interface PinataFile {\n /** Pin identifier */\n id: string;\n /** File name */\n name: string;\n /** IPFS CID */\n cid: string;\n /** File size in bytes */\n size: number;\n /** Creation timestamp */\n createdAt: Date;\n /** Optional metadata */\n metadata?: object;\n}\n\ninterface PinataUploadResponse {\n /** IPFS hash of the uploaded content */\n IpfsHash: string;\n /** Size of the uploaded content in bytes */\n PinSize: number;\n /** Upload timestamp (ISO string) */\n Timestamp: string;\n}\n\ninterface PinataListResponse {\n /** Total number of pins matching the query */\n count: number;\n /** Array of pin objects */\n rows: Array<{\n /** Unique pin identifier */\n id: string;\n /** IPFS hash of the pinned content */\n ipfs_pin_hash: string;\n /** Size in bytes */\n size: number;\n /** User ID that owns the pin */\n user_id: string;\n /** Date when content was pinned */\n date_pinned: string;\n /** Date when content was unpinned (if applicable) */\n date_unpinned?: string;\n /** Pin metadata */\n metadata: {\n /** Optional name for the pin */\n name?: string;\n /** Additional key-value metadata */\n keyvalues?: Record<string, unknown>;\n };\n }>;\n}\n\n/**\n * Provides managed IPFS storage with full-featured API via Pinata\n *\n * @remarks\n * This provider uses Pinata's enhanced IPFS service, which extends standard IPFS\n * with additional features like file listing, deletion (unpinning), and rich metadata.\n * It's the \"it just works\" solution for developers who want full CRUD operations\n * on IPFS without managing infrastructure.\n *\n * @category Storage\n *\n * @example\n * ```typescript\n * const pinataStorage = new PinataStorage({\n * jwt: \"your-pinata-jwt-token\"\n * });\n *\n * // Upload with metadata\n * const cid = await pinataStorage.upload(fileBlob, {\n * name: \"user-avatar.png\",\n * metadata: { userId: \"123\", category: \"avatar\" }\n * });\n *\n * // List files with query\n * const files = await pinataStorage.list({ limit: 10 });\n *\n * // Delete file\n * await pinataStorage.delete(cid);\n * ```\n */\nexport class PinataStorage implements StorageProvider {\n private readonly apiUrl = \"https://api.pinata.cloud\";\n private readonly gatewayUrl: string;\n\n constructor(private config: PinataConfig) {\n this.gatewayUrl = config.gatewayUrl ?? \"https://gateway.pinata.cloud\";\n if (!config.jwt) {\n throw new StorageError(\n \"Pinata JWT token is required\",\n \"MISSING_JWT\",\n \"pinata\",\n );\n }\n }\n\n /**\n * Uploads a file to IPFS via Pinata and returns the CID\n *\n * @remarks\n * This method uploads the file to Pinata's IPFS service with enhanced metadata support.\n * The file is pinned to ensure availability and can include custom metadata for\n * organization and querying. The metadata is stored alongside the file for later retrieval.\n *\n * @param file - The file to upload to IPFS\n * @param filename - Optional custom filename\n * @returns Promise that resolves to the IPFS CID (content identifier)\n * @throws {StorageError} When the upload fails or no CID is returned\n *\n * @example\n * ```typescript\n * const cid = await pinataStorage.upload(fileBlob, {\n * name: \"user-document.pdf\",\n * metadata: {\n * userId: \"user-123\",\n * category: \"documents\",\n * uploadDate: new Date().toISOString()\n * }\n * });\n * console.log(\"File pinned to IPFS:\", cid);\n * ```\n */\n async upload(file: Blob, filename?: string): Promise<StorageUploadResult> {\n try {\n const fileName = filename ?? `vana-file-${Date.now()}.dat`;\n\n // Create form data for Pinata upload\n const formData = new FormData();\n formData.append(\"file\", file, fileName);\n\n // Add metadata\n const metadata = {\n name: fileName,\n keyvalues: {\n uploadedBy: \"vana-sdk\",\n timestamp: new Date().toISOString(),\n },\n };\n formData.append(\"pinataMetadata\", JSON.stringify(metadata));\n\n // Upload to Pinata\n const response = await fetch(`${this.apiUrl}/pinning/pinFileToIPFS`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.config.jwt}`,\n },\n body: formData,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new StorageError(\n `Pinata upload failed: ${errorText}`,\n \"UPLOAD_FAILED\",\n \"pinata\",\n );\n }\n\n const result = (await response.json()) as PinataUploadResponse;\n const ipfsHash = result.IpfsHash;\n\n if (!ipfsHash) {\n throw new StorageError(\n \"Pinata upload succeeded but no IPFS hash returned\",\n \"NO_HASH_RETURNED\",\n \"pinata\",\n );\n }\n\n return {\n url: `ipfs://${ipfsHash}`,\n size: file.size,\n contentType: file.type ?? \"application/octet-stream\",\n };\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Pinata upload error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"UPLOAD_ERROR\",\n \"pinata\",\n );\n }\n }\n\n async download(cid: string): Promise<Blob> {\n try {\n // Validate CID format\n if (!this.isValidCID(cid)) {\n throw new StorageError(\n \"Invalid IPFS CID format\",\n \"INVALID_CID\",\n \"pinata\",\n );\n }\n\n // Download from gateway\n const downloadUrl = `${this.gatewayUrl}/ipfs/${cid}`;\n const response = await fetch(downloadUrl);\n\n if (!response.ok) {\n throw new StorageError(\n `Failed to download from IPFS: ${response.statusText}`,\n \"DOWNLOAD_FAILED\",\n \"pinata\",\n );\n }\n\n return await response.blob();\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Pinata download error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"DOWNLOAD_ERROR\",\n \"pinata\",\n );\n }\n }\n\n /**\n * Lists files uploaded to Pinata with optional filtering\n *\n * @remarks\n * This method retrieves a list of files that have been uploaded to Pinata,\n * filtered to only include files uploaded by the Vana SDK. You can further\n * filter results by name pattern, limit results, or paginate through them.\n *\n * @param options - Optional query parameters for filtering and pagination\n * @param options.limit - Maximum number of results to return (default: 10)\n * @param options.offset - Number of results to skip for pagination\n * @param options.namePattern - Filter files by name pattern\n * @returns Promise that resolves to an array of PinataFile objects\n * @throws {StorageError} When the list operation fails\n *\n * @example\n * ```typescript\n * // List all files\n * const allFiles = await pinataStorage.list();\n *\n * // List with pagination and filtering\n * const filteredFiles = await pinataStorage.list({\n * limit: 20,\n * offset: 10,\n * namePattern: \"document\"\n * });\n *\n * filteredFiles.forEach(file => {\n * console.log(`${file.name} (${file.size} bytes): ${file.cid}`);\n * });\n * ```\n */\n async list(options?: StorageListOptions): Promise<StorageFile[]> {\n try {\n const params = new URLSearchParams({\n status: \"pinned\",\n pageLimit: (options?.limit ?? 10).toString(),\n metadata: JSON.stringify({\n keyvalues: {\n uploadedBy: \"vana-sdk\",\n },\n }),\n });\n\n if (options?.offset) {\n params.set(\"pageOffset\", options.offset.toString());\n }\n\n if (options?.namePattern) {\n params.set(\"metadata[name]\", options.namePattern);\n }\n\n const response = await fetch(`${this.apiUrl}/data/pinList?${params}`, {\n headers: {\n Authorization: `Bearer ${this.config.jwt}`,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new StorageError(\n `Failed to list Pinata files: ${errorText}`,\n \"LIST_FAILED\",\n \"pinata\",\n );\n }\n\n const result = (await response.json()) as PinataListResponse;\n\n return result.rows.map((pin) => ({\n id: pin.id,\n name: pin.metadata?.name ?? \"Unnamed\",\n url: `ipfs://${pin.ipfs_pin_hash}`,\n size: parseInt(String(pin.size), 10) || 0,\n contentType: \"application/octet-stream\", // Pinata doesn't store content type\n createdAt: new Date(pin.date_pinned),\n metadata: pin.metadata?.keyvalues ?? {},\n }));\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Pinata list error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"LIST_ERROR\",\n \"pinata\",\n );\n }\n }\n\n /**\n * Deletes a file from Pinata by unpinning it from IPFS\n *\n * @remarks\n * This method removes the file from your Pinata account by unpinning it,\n * which means it will no longer be guaranteed to be available on the IPFS network.\n * Note that if the file is pinned elsewhere or cached by other nodes, it may still\n * be accessible for some time.\n *\n * @param url - The IPFS URL or content identifier of the file to delete\n * @returns Promise that resolves when the file is successfully unpinned\n * @throws {StorageError} When the deletion fails or CID format is invalid\n *\n * @example\n * ```typescript\n * // Delete a file by CID\n * await pinataStorage.delete(\"QmTzQ1JRkWErjk39mryYw2WVrgBMe2B36gRq8GCL8qCACj\");\n * console.log(\"File unpinned from Pinata\");\n *\n * // Delete after listing\n * const files = await pinataStorage.list();\n * for (const file of files) {\n * if (file.name.includes(\"temp\")) {\n * await pinataStorage.delete(file.cid);\n * }\n * }\n * ```\n */\n async delete(url: string): Promise<boolean> {\n try {\n // Extract CID from URL or use as-is\n const cid = this.extractCidFromUrl(url);\n\n // Validate CID format\n if (!this.isValidCID(cid)) {\n throw new StorageError(\n \"Invalid IPFS CID format\",\n \"INVALID_CID\",\n \"pinata\",\n );\n }\n\n const response = await fetch(`${this.apiUrl}/pinning/unpin/${cid}`, {\n method: \"DELETE\",\n headers: {\n Authorization: `Bearer ${this.config.jwt}`,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new StorageError(\n `Failed to delete from Pinata: ${errorText}`,\n \"DELETE_FAILED\",\n \"pinata\",\n );\n }\n\n return true;\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Pinata delete error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"DELETE_ERROR\",\n \"pinata\",\n );\n }\n }\n\n getConfig(): StorageProviderConfig {\n return {\n name: \"Pinata IPFS\",\n type: \"pinata\",\n requiresAuth: true,\n features: {\n upload: true,\n download: true,\n list: true,\n delete: true,\n },\n };\n }\n\n /**\n * Extract CID from URL or return as-is\n *\n * @param url - URL or CID string\n * @returns CID string\n */\n private extractCidFromUrl(url: string): string {\n // If it's already a CID (not a URL), return as-is\n if (!url.includes(\"/\")) {\n return url;\n }\n\n // Extract CID from gateway URL\n const cidMatch = url.match(/\\/ipfs\\/([a-zA-Z0-9]+)/);\n if (cidMatch) {\n return cidMatch[1];\n }\n\n // If no match, assume it's a CID\n return url;\n }\n\n /**\n * Basic CID validation\n *\n * @param cid - Content identifier to validate\n * @returns True if CID appears valid\n */\n private isValidCID(cid: string): boolean {\n // Basic validation: CIDs typically start with 'Qm' or 'ba' and contain alphanumeric characters\n return (\n /^[a-zA-Z0-9]{10,}$/.test(cid) &&\n (cid.startsWith(\"Qm\") || cid.startsWith(\"ba\") || cid.includes(\"Test\"))\n );\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/storage/providers/pinata.ts"],"sourcesContent":["import {\n StorageError,\n type StorageProvider,\n type StorageUploadResult,\n type StorageFile,\n type StorageListOptions,\n type StorageProviderConfig,\n} from \"../index\";\n\nexport interface PinataConfig {\n /** Pinata JWT token for authentication */\n jwt: string;\n /** Optional custom gateway URL (defaults to https://gateway.pinata.cloud) */\n gatewayUrl?: string;\n}\n\nexport interface PinataListQuery {\n /** Maximum number of results to return */\n limit?: number;\n /** Offset for pagination */\n offset?: number;\n /** Filter by name pattern */\n namePattern?: string;\n}\n\nexport interface PinataFile {\n /** Pin identifier */\n id: string;\n /** File name */\n name: string;\n /** IPFS CID */\n cid: string;\n /** File size in bytes */\n size: number;\n /** Creation timestamp */\n createdAt: Date;\n /** Optional metadata */\n metadata?: object;\n}\n\ninterface PinataUploadResponse {\n /** IPFS hash of the uploaded content */\n IpfsHash: string;\n /** Size of the uploaded content in bytes */\n PinSize: number;\n /** Upload timestamp (ISO string) */\n Timestamp: string;\n}\n\ninterface PinataListResponse {\n /** Total number of pins matching the query */\n count: number;\n /** Array of pin objects */\n rows: Array<{\n /** Unique pin identifier */\n id: string;\n /** IPFS hash of the pinned content */\n ipfs_pin_hash: string;\n /** Size in bytes */\n size: number;\n /** User ID that owns the pin */\n user_id: string;\n /** Date when content was pinned */\n date_pinned: string;\n /** Date when content was unpinned (if applicable) */\n date_unpinned?: string;\n /** Pin metadata */\n metadata: {\n /** Optional name for the pin */\n name?: string;\n /** Additional key-value metadata */\n keyvalues?: Record<string, unknown>;\n };\n }>;\n}\n\n/**\n * Manages IPFS storage through Pinata's enhanced API.\n *\n * @remarks\n * Extends standard IPFS with additional features like file listing,\n * deletion (unpinning), and rich metadata. Production-ready managed\n * service with guaranteed availability. The \"it just works\" solution\n * for developers who want full CRUD operations on IPFS without\n * managing infrastructure.\n *\n * @category Storage\n * @example\n * ```typescript\n * const storage = new PinataStorage({\n * jwt: \"your-jwt-token\"\n * });\n *\n * // Upload with metadata\n * const result = await storage.upload(blob, \"file.json\");\n * console.log(`Pinned at: ${result.url}`);\n *\n * // List and manage files\n * const files = await storage.list({ limit: 10 });\n *\n * // Delete file\n * await pinataStorage.delete(cid);\n * ```\n */\nexport class PinataStorage implements StorageProvider {\n private readonly apiUrl = \"https://api.pinata.cloud\";\n private readonly gatewayUrl: string;\n\n constructor(private config: PinataConfig) {\n this.gatewayUrl = config.gatewayUrl ?? \"https://gateway.pinata.cloud\";\n if (!config.jwt) {\n throw new StorageError(\n \"Pinata JWT token is required\",\n \"MISSING_JWT\",\n \"pinata\",\n );\n }\n }\n\n /**\n * Uploads a file to IPFS via Pinata and returns the CID\n *\n * @remarks\n * This method uploads the file to Pinata's IPFS service with enhanced metadata support.\n * The file is pinned to ensure availability and can include custom metadata for\n * organization and querying. The metadata is stored alongside the file for later retrieval.\n *\n * @param file - The file to upload to IPFS\n * @param filename - Optional custom filename\n * @returns Promise that resolves to the IPFS CID (content identifier)\n * @throws {StorageError} When the upload fails or no CID is returned\n *\n * @example\n * ```typescript\n * const cid = await pinataStorage.upload(fileBlob, {\n * name: \"user-document.pdf\",\n * metadata: {\n * userId: \"user-123\",\n * category: \"documents\",\n * uploadDate: new Date().toISOString()\n * }\n * });\n * console.log(\"File pinned to IPFS:\", cid);\n * ```\n */\n async upload(file: Blob, filename?: string): Promise<StorageUploadResult> {\n try {\n const fileName = filename ?? `vana-file-${Date.now()}.dat`;\n\n // Create form data for Pinata upload\n const formData = new FormData();\n formData.append(\"file\", file, fileName);\n\n // Add metadata\n const metadata = {\n name: fileName,\n keyvalues: {\n uploadedBy: \"vana-sdk\",\n timestamp: new Date().toISOString(),\n },\n };\n formData.append(\"pinataMetadata\", JSON.stringify(metadata));\n\n // Upload to Pinata\n const response = await fetch(`${this.apiUrl}/pinning/pinFileToIPFS`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.config.jwt}`,\n },\n body: formData,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new StorageError(\n `Pinata upload failed: ${errorText}`,\n \"UPLOAD_FAILED\",\n \"pinata\",\n );\n }\n\n const result = (await response.json()) as PinataUploadResponse;\n const ipfsHash = result.IpfsHash;\n\n if (!ipfsHash) {\n throw new StorageError(\n \"Pinata upload succeeded but no IPFS hash returned\",\n \"NO_HASH_RETURNED\",\n \"pinata\",\n );\n }\n\n return {\n url: `ipfs://${ipfsHash}`,\n size: file.size,\n contentType: file.type ?? \"application/octet-stream\",\n };\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Pinata upload error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"UPLOAD_ERROR\",\n \"pinata\",\n );\n }\n }\n\n async download(cid: string): Promise<Blob> {\n try {\n // Validate CID format\n if (!this.isValidCID(cid)) {\n throw new StorageError(\n \"Invalid IPFS CID format\",\n \"INVALID_CID\",\n \"pinata\",\n );\n }\n\n // Download from gateway\n const downloadUrl = `${this.gatewayUrl}/ipfs/${cid}`;\n const response = await fetch(downloadUrl);\n\n if (!response.ok) {\n throw new StorageError(\n `Failed to download from IPFS: ${response.statusText}`,\n \"DOWNLOAD_FAILED\",\n \"pinata\",\n );\n }\n\n return await response.blob();\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Pinata download error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"DOWNLOAD_ERROR\",\n \"pinata\",\n );\n }\n }\n\n /**\n * Lists files uploaded to Pinata with optional filtering\n *\n * @remarks\n * This method retrieves a list of files that have been uploaded to Pinata,\n * filtered to only include files uploaded by the Vana SDK. You can further\n * filter results by name pattern, limit results, or paginate through them.\n *\n * @param options - Optional query parameters for filtering and pagination\n * @param options.limit - Maximum number of results to return (default: 10)\n * @param options.offset - Number of results to skip for pagination\n * @param options.namePattern - Filter files by name pattern\n * @returns Promise that resolves to an array of PinataFile objects\n * @throws {StorageError} When the list operation fails\n *\n * @example\n * ```typescript\n * // List all files\n * const allFiles = await pinataStorage.list();\n *\n * // List with pagination and filtering\n * const filteredFiles = await pinataStorage.list({\n * limit: 20,\n * offset: 10,\n * namePattern: \"document\"\n * });\n *\n * filteredFiles.forEach(file => {\n * console.log(`${file.name} (${file.size} bytes): ${file.cid}`);\n * });\n * ```\n */\n async list(options?: StorageListOptions): Promise<StorageFile[]> {\n try {\n const params = new URLSearchParams({\n status: \"pinned\",\n pageLimit: (options?.limit ?? 10).toString(),\n metadata: JSON.stringify({\n keyvalues: {\n uploadedBy: \"vana-sdk\",\n },\n }),\n });\n\n if (options?.offset) {\n params.set(\"pageOffset\", options.offset.toString());\n }\n\n if (options?.namePattern) {\n params.set(\"metadata[name]\", options.namePattern);\n }\n\n const response = await fetch(`${this.apiUrl}/data/pinList?${params}`, {\n headers: {\n Authorization: `Bearer ${this.config.jwt}`,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new StorageError(\n `Failed to list Pinata files: ${errorText}`,\n \"LIST_FAILED\",\n \"pinata\",\n );\n }\n\n const result = (await response.json()) as PinataListResponse;\n\n return result.rows.map((pin) => ({\n id: pin.id,\n name: pin.metadata?.name ?? \"Unnamed\",\n url: `ipfs://${pin.ipfs_pin_hash}`,\n size: parseInt(String(pin.size), 10) || 0,\n contentType: \"application/octet-stream\", // Pinata doesn't store content type\n createdAt: new Date(pin.date_pinned),\n metadata: pin.metadata?.keyvalues ?? {},\n }));\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Pinata list error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"LIST_ERROR\",\n \"pinata\",\n );\n }\n }\n\n /**\n * Deletes a file from Pinata by unpinning it from IPFS\n *\n * @remarks\n * This method removes the file from your Pinata account by unpinning it,\n * which means it will no longer be guaranteed to be available on the IPFS network.\n * Note that if the file is pinned elsewhere or cached by other nodes, it may still\n * be accessible for some time.\n *\n * @param url - The IPFS URL or content identifier of the file to delete\n * @returns Promise that resolves when the file is successfully unpinned\n * @throws {StorageError} When the deletion fails or CID format is invalid\n *\n * @example\n * ```typescript\n * // Delete a file by CID\n * await pinataStorage.delete(\"QmTzQ1JRkWErjk39mryYw2WVrgBMe2B36gRq8GCL8qCACj\");\n * console.log(\"File unpinned from Pinata\");\n *\n * // Delete after listing\n * const files = await pinataStorage.list();\n * for (const file of files) {\n * if (file.name.includes(\"temp\")) {\n * await pinataStorage.delete(file.cid);\n * }\n * }\n * ```\n */\n async delete(url: string): Promise<boolean> {\n try {\n // Extract CID from URL or use as-is\n const cid = this.extractCidFromUrl(url);\n\n // Validate CID format\n if (!this.isValidCID(cid)) {\n throw new StorageError(\n \"Invalid IPFS CID format\",\n \"INVALID_CID\",\n \"pinata\",\n );\n }\n\n const response = await fetch(`${this.apiUrl}/pinning/unpin/${cid}`, {\n method: \"DELETE\",\n headers: {\n Authorization: `Bearer ${this.config.jwt}`,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new StorageError(\n `Failed to delete from Pinata: ${errorText}`,\n \"DELETE_FAILED\",\n \"pinata\",\n );\n }\n\n return true;\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Pinata delete error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"DELETE_ERROR\",\n \"pinata\",\n );\n }\n }\n\n getConfig(): StorageProviderConfig {\n return {\n name: \"Pinata IPFS\",\n type: \"pinata\",\n requiresAuth: true,\n features: {\n upload: true,\n download: true,\n list: true,\n delete: true,\n },\n };\n }\n\n /**\n * Extract CID from URL or return as-is\n *\n * @param url - URL or CID string\n * @returns CID string\n */\n private extractCidFromUrl(url: string): string {\n // If it's already a CID (not a URL), return as-is\n if (!url.includes(\"/\")) {\n return url;\n }\n\n // Extract CID from gateway URL\n const cidMatch = url.match(/\\/ipfs\\/([a-zA-Z0-9]+)/);\n if (cidMatch) {\n return cidMatch[1];\n }\n\n // If no match, assume it's a CID\n return url;\n }\n\n /**\n * Basic CID validation\n *\n * @param cid - Content identifier to validate\n * @returns True if CID appears valid\n */\n private isValidCID(cid: string): boolean {\n // Basic validation: CIDs typically start with 'Qm' or 'ba' and contain alphanumeric characters\n return (\n /^[a-zA-Z0-9]{10,}$/.test(cid) &&\n (cid.startsWith(\"Qm\") || cid.startsWith(\"ba\") || cid.includes(\"Test\"))\n );\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -28,30 +28,28 @@ export interface PinataFile {
|
|
|
28
28
|
metadata?: object;
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
31
|
+
* Manages IPFS storage through Pinata's enhanced API.
|
|
32
32
|
*
|
|
33
33
|
* @remarks
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* on IPFS without
|
|
34
|
+
* Extends standard IPFS with additional features like file listing,
|
|
35
|
+
* deletion (unpinning), and rich metadata. Production-ready managed
|
|
36
|
+
* service with guaranteed availability. The "it just works" solution
|
|
37
|
+
* for developers who want full CRUD operations on IPFS without
|
|
38
|
+
* managing infrastructure.
|
|
38
39
|
*
|
|
39
40
|
* @category Storage
|
|
40
|
-
*
|
|
41
41
|
* @example
|
|
42
42
|
* ```typescript
|
|
43
|
-
* const
|
|
44
|
-
* jwt: "your-
|
|
43
|
+
* const storage = new PinataStorage({
|
|
44
|
+
* jwt: "your-jwt-token"
|
|
45
45
|
* });
|
|
46
46
|
*
|
|
47
47
|
* // Upload with metadata
|
|
48
|
-
* const
|
|
49
|
-
*
|
|
50
|
-
* metadata: { userId: "123", category: "avatar" }
|
|
51
|
-
* });
|
|
48
|
+
* const result = await storage.upload(blob, "file.json");
|
|
49
|
+
* console.log(`Pinned at: ${result.url}`);
|
|
52
50
|
*
|
|
53
|
-
* // List
|
|
54
|
-
* const files = await
|
|
51
|
+
* // List and manage files
|
|
52
|
+
* const files = await storage.list({ limit: 10 });
|
|
55
53
|
*
|
|
56
54
|
* // Delete file
|
|
57
55
|
* await pinataStorage.delete(cid);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/storage/providers/pinata.ts"],"sourcesContent":["import {\n StorageError,\n type StorageProvider,\n type StorageUploadResult,\n type StorageFile,\n type StorageListOptions,\n type StorageProviderConfig,\n} from \"../index\";\n\nexport interface PinataConfig {\n /** Pinata JWT token for authentication */\n jwt: string;\n /** Optional custom gateway URL (defaults to https://gateway.pinata.cloud) */\n gatewayUrl?: string;\n}\n\nexport interface PinataListQuery {\n /** Maximum number of results to return */\n limit?: number;\n /** Offset for pagination */\n offset?: number;\n /** Filter by name pattern */\n namePattern?: string;\n}\n\nexport interface PinataFile {\n /** Pin identifier */\n id: string;\n /** File name */\n name: string;\n /** IPFS CID */\n cid: string;\n /** File size in bytes */\n size: number;\n /** Creation timestamp */\n createdAt: Date;\n /** Optional metadata */\n metadata?: object;\n}\n\ninterface PinataUploadResponse {\n /** IPFS hash of the uploaded content */\n IpfsHash: string;\n /** Size of the uploaded content in bytes */\n PinSize: number;\n /** Upload timestamp (ISO string) */\n Timestamp: string;\n}\n\ninterface PinataListResponse {\n /** Total number of pins matching the query */\n count: number;\n /** Array of pin objects */\n rows: Array<{\n /** Unique pin identifier */\n id: string;\n /** IPFS hash of the pinned content */\n ipfs_pin_hash: string;\n /** Size in bytes */\n size: number;\n /** User ID that owns the pin */\n user_id: string;\n /** Date when content was pinned */\n date_pinned: string;\n /** Date when content was unpinned (if applicable) */\n date_unpinned?: string;\n /** Pin metadata */\n metadata: {\n /** Optional name for the pin */\n name?: string;\n /** Additional key-value metadata */\n keyvalues?: Record<string, unknown>;\n };\n }>;\n}\n\n/**\n * Provides managed IPFS storage with full-featured API via Pinata\n *\n * @remarks\n * This provider uses Pinata's enhanced IPFS service, which extends standard IPFS\n * with additional features like file listing, deletion (unpinning), and rich metadata.\n * It's the \"it just works\" solution for developers who want full CRUD operations\n * on IPFS without managing infrastructure.\n *\n * @category Storage\n *\n * @example\n * ```typescript\n * const pinataStorage = new PinataStorage({\n * jwt: \"your-pinata-jwt-token\"\n * });\n *\n * // Upload with metadata\n * const cid = await pinataStorage.upload(fileBlob, {\n * name: \"user-avatar.png\",\n * metadata: { userId: \"123\", category: \"avatar\" }\n * });\n *\n * // List files with query\n * const files = await pinataStorage.list({ limit: 10 });\n *\n * // Delete file\n * await pinataStorage.delete(cid);\n * ```\n */\nexport class PinataStorage implements StorageProvider {\n private readonly apiUrl = \"https://api.pinata.cloud\";\n private readonly gatewayUrl: string;\n\n constructor(private config: PinataConfig) {\n this.gatewayUrl = config.gatewayUrl ?? \"https://gateway.pinata.cloud\";\n if (!config.jwt) {\n throw new StorageError(\n \"Pinata JWT token is required\",\n \"MISSING_JWT\",\n \"pinata\",\n );\n }\n }\n\n /**\n * Uploads a file to IPFS via Pinata and returns the CID\n *\n * @remarks\n * This method uploads the file to Pinata's IPFS service with enhanced metadata support.\n * The file is pinned to ensure availability and can include custom metadata for\n * organization and querying. The metadata is stored alongside the file for later retrieval.\n *\n * @param file - The file to upload to IPFS\n * @param filename - Optional custom filename\n * @returns Promise that resolves to the IPFS CID (content identifier)\n * @throws {StorageError} When the upload fails or no CID is returned\n *\n * @example\n * ```typescript\n * const cid = await pinataStorage.upload(fileBlob, {\n * name: \"user-document.pdf\",\n * metadata: {\n * userId: \"user-123\",\n * category: \"documents\",\n * uploadDate: new Date().toISOString()\n * }\n * });\n * console.log(\"File pinned to IPFS:\", cid);\n * ```\n */\n async upload(file: Blob, filename?: string): Promise<StorageUploadResult> {\n try {\n const fileName = filename ?? `vana-file-${Date.now()}.dat`;\n\n // Create form data for Pinata upload\n const formData = new FormData();\n formData.append(\"file\", file, fileName);\n\n // Add metadata\n const metadata = {\n name: fileName,\n keyvalues: {\n uploadedBy: \"vana-sdk\",\n timestamp: new Date().toISOString(),\n },\n };\n formData.append(\"pinataMetadata\", JSON.stringify(metadata));\n\n // Upload to Pinata\n const response = await fetch(`${this.apiUrl}/pinning/pinFileToIPFS`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.config.jwt}`,\n },\n body: formData,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new StorageError(\n `Pinata upload failed: ${errorText}`,\n \"UPLOAD_FAILED\",\n \"pinata\",\n );\n }\n\n const result = (await response.json()) as PinataUploadResponse;\n const ipfsHash = result.IpfsHash;\n\n if (!ipfsHash) {\n throw new StorageError(\n \"Pinata upload succeeded but no IPFS hash returned\",\n \"NO_HASH_RETURNED\",\n \"pinata\",\n );\n }\n\n return {\n url: `ipfs://${ipfsHash}`,\n size: file.size,\n contentType: file.type ?? \"application/octet-stream\",\n };\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Pinata upload error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"UPLOAD_ERROR\",\n \"pinata\",\n );\n }\n }\n\n async download(cid: string): Promise<Blob> {\n try {\n // Validate CID format\n if (!this.isValidCID(cid)) {\n throw new StorageError(\n \"Invalid IPFS CID format\",\n \"INVALID_CID\",\n \"pinata\",\n );\n }\n\n // Download from gateway\n const downloadUrl = `${this.gatewayUrl}/ipfs/${cid}`;\n const response = await fetch(downloadUrl);\n\n if (!response.ok) {\n throw new StorageError(\n `Failed to download from IPFS: ${response.statusText}`,\n \"DOWNLOAD_FAILED\",\n \"pinata\",\n );\n }\n\n return await response.blob();\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Pinata download error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"DOWNLOAD_ERROR\",\n \"pinata\",\n );\n }\n }\n\n /**\n * Lists files uploaded to Pinata with optional filtering\n *\n * @remarks\n * This method retrieves a list of files that have been uploaded to Pinata,\n * filtered to only include files uploaded by the Vana SDK. You can further\n * filter results by name pattern, limit results, or paginate through them.\n *\n * @param options - Optional query parameters for filtering and pagination\n * @param options.limit - Maximum number of results to return (default: 10)\n * @param options.offset - Number of results to skip for pagination\n * @param options.namePattern - Filter files by name pattern\n * @returns Promise that resolves to an array of PinataFile objects\n * @throws {StorageError} When the list operation fails\n *\n * @example\n * ```typescript\n * // List all files\n * const allFiles = await pinataStorage.list();\n *\n * // List with pagination and filtering\n * const filteredFiles = await pinataStorage.list({\n * limit: 20,\n * offset: 10,\n * namePattern: \"document\"\n * });\n *\n * filteredFiles.forEach(file => {\n * console.log(`${file.name} (${file.size} bytes): ${file.cid}`);\n * });\n * ```\n */\n async list(options?: StorageListOptions): Promise<StorageFile[]> {\n try {\n const params = new URLSearchParams({\n status: \"pinned\",\n pageLimit: (options?.limit ?? 10).toString(),\n metadata: JSON.stringify({\n keyvalues: {\n uploadedBy: \"vana-sdk\",\n },\n }),\n });\n\n if (options?.offset) {\n params.set(\"pageOffset\", options.offset.toString());\n }\n\n if (options?.namePattern) {\n params.set(\"metadata[name]\", options.namePattern);\n }\n\n const response = await fetch(`${this.apiUrl}/data/pinList?${params}`, {\n headers: {\n Authorization: `Bearer ${this.config.jwt}`,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new StorageError(\n `Failed to list Pinata files: ${errorText}`,\n \"LIST_FAILED\",\n \"pinata\",\n );\n }\n\n const result = (await response.json()) as PinataListResponse;\n\n return result.rows.map((pin) => ({\n id: pin.id,\n name: pin.metadata?.name ?? \"Unnamed\",\n url: `ipfs://${pin.ipfs_pin_hash}`,\n size: parseInt(String(pin.size), 10) || 0,\n contentType: \"application/octet-stream\", // Pinata doesn't store content type\n createdAt: new Date(pin.date_pinned),\n metadata: pin.metadata?.keyvalues ?? {},\n }));\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Pinata list error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"LIST_ERROR\",\n \"pinata\",\n );\n }\n }\n\n /**\n * Deletes a file from Pinata by unpinning it from IPFS\n *\n * @remarks\n * This method removes the file from your Pinata account by unpinning it,\n * which means it will no longer be guaranteed to be available on the IPFS network.\n * Note that if the file is pinned elsewhere or cached by other nodes, it may still\n * be accessible for some time.\n *\n * @param url - The IPFS URL or content identifier of the file to delete\n * @returns Promise that resolves when the file is successfully unpinned\n * @throws {StorageError} When the deletion fails or CID format is invalid\n *\n * @example\n * ```typescript\n * // Delete a file by CID\n * await pinataStorage.delete(\"QmTzQ1JRkWErjk39mryYw2WVrgBMe2B36gRq8GCL8qCACj\");\n * console.log(\"File unpinned from Pinata\");\n *\n * // Delete after listing\n * const files = await pinataStorage.list();\n * for (const file of files) {\n * if (file.name.includes(\"temp\")) {\n * await pinataStorage.delete(file.cid);\n * }\n * }\n * ```\n */\n async delete(url: string): Promise<boolean> {\n try {\n // Extract CID from URL or use as-is\n const cid = this.extractCidFromUrl(url);\n\n // Validate CID format\n if (!this.isValidCID(cid)) {\n throw new StorageError(\n \"Invalid IPFS CID format\",\n \"INVALID_CID\",\n \"pinata\",\n );\n }\n\n const response = await fetch(`${this.apiUrl}/pinning/unpin/${cid}`, {\n method: \"DELETE\",\n headers: {\n Authorization: `Bearer ${this.config.jwt}`,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new StorageError(\n `Failed to delete from Pinata: ${errorText}`,\n \"DELETE_FAILED\",\n \"pinata\",\n );\n }\n\n return true;\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Pinata delete error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"DELETE_ERROR\",\n \"pinata\",\n );\n }\n }\n\n getConfig(): StorageProviderConfig {\n return {\n name: \"Pinata IPFS\",\n type: \"pinata\",\n requiresAuth: true,\n features: {\n upload: true,\n download: true,\n list: true,\n delete: true,\n },\n };\n }\n\n /**\n * Extract CID from URL or return as-is\n *\n * @param url - URL or CID string\n * @returns CID string\n */\n private extractCidFromUrl(url: string): string {\n // If it's already a CID (not a URL), return as-is\n if (!url.includes(\"/\")) {\n return url;\n }\n\n // Extract CID from gateway URL\n const cidMatch = url.match(/\\/ipfs\\/([a-zA-Z0-9]+)/);\n if (cidMatch) {\n return cidMatch[1];\n }\n\n // If no match, assume it's a CID\n return url;\n }\n\n /**\n * Basic CID validation\n *\n * @param cid - Content identifier to validate\n * @returns True if CID appears valid\n */\n private isValidCID(cid: string): boolean {\n // Basic validation: CIDs typically start with 'Qm' or 'ba' and contain alphanumeric characters\n return (\n /^[a-zA-Z0-9]{10,}$/.test(cid) &&\n (cid.startsWith(\"Qm\") || cid.startsWith(\"ba\") || cid.includes(\"Test\"))\n );\n }\n}\n"],"mappings":"AAAA;AAAA,EACE;AAAA,OAMK;AAmGA,MAAM,cAAyC;AAAA,EAIpD,YAAoB,QAAsB;AAAtB;AAClB,SAAK,aAAa,OAAO,cAAc;AACvC,QAAI,CAAC,OAAO,KAAK;AACf,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAZiB,SAAS;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuCjB,MAAM,OAAO,MAAY,UAAiD;AACxE,QAAI;AACF,YAAM,WAAW,YAAY,aAAa,KAAK,IAAI,CAAC;AAGpD,YAAM,WAAW,IAAI,SAAS;AAC9B,eAAS,OAAO,QAAQ,MAAM,QAAQ;AAGtC,YAAM,WAAW;AAAA,QACf,MAAM;AAAA,QACN,WAAW;AAAA,UACT,YAAY;AAAA,UACZ,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QACpC;AAAA,MACF;AACA,eAAS,OAAO,kBAAkB,KAAK,UAAU,QAAQ,CAAC;AAG1D,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,MAAM,0BAA0B;AAAA,QACnE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,eAAe,UAAU,KAAK,OAAO,GAAG;AAAA,QAC1C;AAAA,QACA,MAAM;AAAA,MACR,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,YAAY,MAAM,SAAS,KAAK;AACtC,cAAM,IAAI;AAAA,UACR,yBAAyB,SAAS;AAAA,UAClC;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAEA,YAAM,SAAU,MAAM,SAAS,KAAK;AACpC,YAAM,WAAW,OAAO;AAExB,UAAI,CAAC,UAAU;AACb,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAEA,aAAO;AAAA,QACL,KAAK,UAAU,QAAQ;AAAA,QACvB,MAAM,KAAK;AAAA,QACX,aAAa,KAAK,QAAQ;AAAA,MAC5B;AAAA,IACF,SAAS,OAAO;AACd,UAAI,iBAAiB,cAAc;AACjC,cAAM;AAAA,MACR;AACA,YAAM,IAAI;AAAA,QACR,wBAAwB,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,QAChF;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,KAA4B;AACzC,QAAI;AAEF,UAAI,CAAC,KAAK,WAAW,GAAG,GAAG;AACzB,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAGA,YAAM,cAAc,GAAG,KAAK,UAAU,SAAS,GAAG;AAClD,YAAM,WAAW,MAAM,MAAM,WAAW;AAExC,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI;AAAA,UACR,iCAAiC,SAAS,UAAU;AAAA,UACpD;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAEA,aAAO,MAAM,SAAS,KAAK;AAAA,IAC7B,SAAS,OAAO;AACd,UAAI,iBAAiB,cAAc;AACjC,cAAM;AAAA,MACR;AACA,YAAM,IAAI;AAAA,QACR,0BAA0B,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,QAClF;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkCA,MAAM,KAAK,SAAsD;AAC/D,QAAI;AACF,YAAM,SAAS,IAAI,gBAAgB;AAAA,QACjC,QAAQ;AAAA,QACR,YAAY,SAAS,SAAS,IAAI,SAAS;AAAA,QAC3C,UAAU,KAAK,UAAU;AAAA,UACvB,WAAW;AAAA,YACT,YAAY;AAAA,UACd;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAED,UAAI,SAAS,QAAQ;AACnB,eAAO,IAAI,cAAc,QAAQ,OAAO,SAAS,CAAC;AAAA,MACpD;AAEA,UAAI,SAAS,aAAa;AACxB,eAAO,IAAI,kBAAkB,QAAQ,WAAW;AAAA,MAClD;AAEA,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,MAAM,iBAAiB,MAAM,IAAI;AAAA,QACpE,SAAS;AAAA,UACP,eAAe,UAAU,KAAK,OAAO,GAAG;AAAA,QAC1C;AAAA,MACF,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,YAAY,MAAM,SAAS,KAAK;AACtC,cAAM,IAAI;AAAA,UACR,gCAAgC,SAAS;AAAA,UACzC;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAEA,YAAM,SAAU,MAAM,SAAS,KAAK;AAEpC,aAAO,OAAO,KAAK,IAAI,CAAC,SAAS;AAAA,QAC/B,IAAI,IAAI;AAAA,QACR,MAAM,IAAI,UAAU,QAAQ;AAAA,QAC5B,KAAK,UAAU,IAAI,aAAa;AAAA,QAChC,MAAM,SAAS,OAAO,IAAI,IAAI,GAAG,EAAE,KAAK;AAAA,QACxC,aAAa;AAAA;AAAA,QACb,WAAW,IAAI,KAAK,IAAI,WAAW;AAAA,QACnC,UAAU,IAAI,UAAU,aAAa,CAAC;AAAA,MACxC,EAAE;AAAA,IACJ,SAAS,OAAO;AACd,UAAI,iBAAiB,cAAc;AACjC,cAAM;AAAA,MACR;AACA,YAAM,IAAI;AAAA,QACR,sBAAsB,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,QAC9E;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BA,MAAM,OAAO,KAA+B;AAC1C,QAAI;AAEF,YAAM,MAAM,KAAK,kBAAkB,GAAG;AAGtC,UAAI,CAAC,KAAK,WAAW,GAAG,GAAG;AACzB,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAEA,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,MAAM,kBAAkB,GAAG,IAAI;AAAA,QAClE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,eAAe,UAAU,KAAK,OAAO,GAAG;AAAA,QAC1C;AAAA,MACF,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,YAAY,MAAM,SAAS,KAAK;AACtC,cAAM,IAAI;AAAA,UACR,iCAAiC,SAAS;AAAA,UAC1C;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAEA,aAAO;AAAA,IACT,SAAS,OAAO;AACd,UAAI,iBAAiB,cAAc;AACjC,cAAM;AAAA,MACR;AACA,YAAM,IAAI;AAAA,QACR,wBAAwB,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,QAChF;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,YAAmC;AACjC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,MAAM;AAAA,MACN,cAAc;AAAA,MACd,UAAU;AAAA,QACR,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,kBAAkB,KAAqB;AAE7C,QAAI,CAAC,IAAI,SAAS,GAAG,GAAG;AACtB,aAAO;AAAA,IACT;AAGA,UAAM,WAAW,IAAI,MAAM,wBAAwB;AACnD,QAAI,UAAU;AACZ,aAAO,SAAS,CAAC;AAAA,IACnB;AAGA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,WAAW,KAAsB;AAEvC,WACE,qBAAqB,KAAK,GAAG,MAC5B,IAAI,WAAW,IAAI,KAAK,IAAI,WAAW,IAAI,KAAK,IAAI,SAAS,MAAM;AAAA,EAExE;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/storage/providers/pinata.ts"],"sourcesContent":["import {\n StorageError,\n type StorageProvider,\n type StorageUploadResult,\n type StorageFile,\n type StorageListOptions,\n type StorageProviderConfig,\n} from \"../index\";\n\nexport interface PinataConfig {\n /** Pinata JWT token for authentication */\n jwt: string;\n /** Optional custom gateway URL (defaults to https://gateway.pinata.cloud) */\n gatewayUrl?: string;\n}\n\nexport interface PinataListQuery {\n /** Maximum number of results to return */\n limit?: number;\n /** Offset for pagination */\n offset?: number;\n /** Filter by name pattern */\n namePattern?: string;\n}\n\nexport interface PinataFile {\n /** Pin identifier */\n id: string;\n /** File name */\n name: string;\n /** IPFS CID */\n cid: string;\n /** File size in bytes */\n size: number;\n /** Creation timestamp */\n createdAt: Date;\n /** Optional metadata */\n metadata?: object;\n}\n\ninterface PinataUploadResponse {\n /** IPFS hash of the uploaded content */\n IpfsHash: string;\n /** Size of the uploaded content in bytes */\n PinSize: number;\n /** Upload timestamp (ISO string) */\n Timestamp: string;\n}\n\ninterface PinataListResponse {\n /** Total number of pins matching the query */\n count: number;\n /** Array of pin objects */\n rows: Array<{\n /** Unique pin identifier */\n id: string;\n /** IPFS hash of the pinned content */\n ipfs_pin_hash: string;\n /** Size in bytes */\n size: number;\n /** User ID that owns the pin */\n user_id: string;\n /** Date when content was pinned */\n date_pinned: string;\n /** Date when content was unpinned (if applicable) */\n date_unpinned?: string;\n /** Pin metadata */\n metadata: {\n /** Optional name for the pin */\n name?: string;\n /** Additional key-value metadata */\n keyvalues?: Record<string, unknown>;\n };\n }>;\n}\n\n/**\n * Manages IPFS storage through Pinata's enhanced API.\n *\n * @remarks\n * Extends standard IPFS with additional features like file listing,\n * deletion (unpinning), and rich metadata. Production-ready managed\n * service with guaranteed availability. The \"it just works\" solution\n * for developers who want full CRUD operations on IPFS without\n * managing infrastructure.\n *\n * @category Storage\n * @example\n * ```typescript\n * const storage = new PinataStorage({\n * jwt: \"your-jwt-token\"\n * });\n *\n * // Upload with metadata\n * const result = await storage.upload(blob, \"file.json\");\n * console.log(`Pinned at: ${result.url}`);\n *\n * // List and manage files\n * const files = await storage.list({ limit: 10 });\n *\n * // Delete file\n * await pinataStorage.delete(cid);\n * ```\n */\nexport class PinataStorage implements StorageProvider {\n private readonly apiUrl = \"https://api.pinata.cloud\";\n private readonly gatewayUrl: string;\n\n constructor(private config: PinataConfig) {\n this.gatewayUrl = config.gatewayUrl ?? \"https://gateway.pinata.cloud\";\n if (!config.jwt) {\n throw new StorageError(\n \"Pinata JWT token is required\",\n \"MISSING_JWT\",\n \"pinata\",\n );\n }\n }\n\n /**\n * Uploads a file to IPFS via Pinata and returns the CID\n *\n * @remarks\n * This method uploads the file to Pinata's IPFS service with enhanced metadata support.\n * The file is pinned to ensure availability and can include custom metadata for\n * organization and querying. The metadata is stored alongside the file for later retrieval.\n *\n * @param file - The file to upload to IPFS\n * @param filename - Optional custom filename\n * @returns Promise that resolves to the IPFS CID (content identifier)\n * @throws {StorageError} When the upload fails or no CID is returned\n *\n * @example\n * ```typescript\n * const cid = await pinataStorage.upload(fileBlob, {\n * name: \"user-document.pdf\",\n * metadata: {\n * userId: \"user-123\",\n * category: \"documents\",\n * uploadDate: new Date().toISOString()\n * }\n * });\n * console.log(\"File pinned to IPFS:\", cid);\n * ```\n */\n async upload(file: Blob, filename?: string): Promise<StorageUploadResult> {\n try {\n const fileName = filename ?? `vana-file-${Date.now()}.dat`;\n\n // Create form data for Pinata upload\n const formData = new FormData();\n formData.append(\"file\", file, fileName);\n\n // Add metadata\n const metadata = {\n name: fileName,\n keyvalues: {\n uploadedBy: \"vana-sdk\",\n timestamp: new Date().toISOString(),\n },\n };\n formData.append(\"pinataMetadata\", JSON.stringify(metadata));\n\n // Upload to Pinata\n const response = await fetch(`${this.apiUrl}/pinning/pinFileToIPFS`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.config.jwt}`,\n },\n body: formData,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new StorageError(\n `Pinata upload failed: ${errorText}`,\n \"UPLOAD_FAILED\",\n \"pinata\",\n );\n }\n\n const result = (await response.json()) as PinataUploadResponse;\n const ipfsHash = result.IpfsHash;\n\n if (!ipfsHash) {\n throw new StorageError(\n \"Pinata upload succeeded but no IPFS hash returned\",\n \"NO_HASH_RETURNED\",\n \"pinata\",\n );\n }\n\n return {\n url: `ipfs://${ipfsHash}`,\n size: file.size,\n contentType: file.type ?? \"application/octet-stream\",\n };\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Pinata upload error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"UPLOAD_ERROR\",\n \"pinata\",\n );\n }\n }\n\n async download(cid: string): Promise<Blob> {\n try {\n // Validate CID format\n if (!this.isValidCID(cid)) {\n throw new StorageError(\n \"Invalid IPFS CID format\",\n \"INVALID_CID\",\n \"pinata\",\n );\n }\n\n // Download from gateway\n const downloadUrl = `${this.gatewayUrl}/ipfs/${cid}`;\n const response = await fetch(downloadUrl);\n\n if (!response.ok) {\n throw new StorageError(\n `Failed to download from IPFS: ${response.statusText}`,\n \"DOWNLOAD_FAILED\",\n \"pinata\",\n );\n }\n\n return await response.blob();\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Pinata download error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"DOWNLOAD_ERROR\",\n \"pinata\",\n );\n }\n }\n\n /**\n * Lists files uploaded to Pinata with optional filtering\n *\n * @remarks\n * This method retrieves a list of files that have been uploaded to Pinata,\n * filtered to only include files uploaded by the Vana SDK. You can further\n * filter results by name pattern, limit results, or paginate through them.\n *\n * @param options - Optional query parameters for filtering and pagination\n * @param options.limit - Maximum number of results to return (default: 10)\n * @param options.offset - Number of results to skip for pagination\n * @param options.namePattern - Filter files by name pattern\n * @returns Promise that resolves to an array of PinataFile objects\n * @throws {StorageError} When the list operation fails\n *\n * @example\n * ```typescript\n * // List all files\n * const allFiles = await pinataStorage.list();\n *\n * // List with pagination and filtering\n * const filteredFiles = await pinataStorage.list({\n * limit: 20,\n * offset: 10,\n * namePattern: \"document\"\n * });\n *\n * filteredFiles.forEach(file => {\n * console.log(`${file.name} (${file.size} bytes): ${file.cid}`);\n * });\n * ```\n */\n async list(options?: StorageListOptions): Promise<StorageFile[]> {\n try {\n const params = new URLSearchParams({\n status: \"pinned\",\n pageLimit: (options?.limit ?? 10).toString(),\n metadata: JSON.stringify({\n keyvalues: {\n uploadedBy: \"vana-sdk\",\n },\n }),\n });\n\n if (options?.offset) {\n params.set(\"pageOffset\", options.offset.toString());\n }\n\n if (options?.namePattern) {\n params.set(\"metadata[name]\", options.namePattern);\n }\n\n const response = await fetch(`${this.apiUrl}/data/pinList?${params}`, {\n headers: {\n Authorization: `Bearer ${this.config.jwt}`,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new StorageError(\n `Failed to list Pinata files: ${errorText}`,\n \"LIST_FAILED\",\n \"pinata\",\n );\n }\n\n const result = (await response.json()) as PinataListResponse;\n\n return result.rows.map((pin) => ({\n id: pin.id,\n name: pin.metadata?.name ?? \"Unnamed\",\n url: `ipfs://${pin.ipfs_pin_hash}`,\n size: parseInt(String(pin.size), 10) || 0,\n contentType: \"application/octet-stream\", // Pinata doesn't store content type\n createdAt: new Date(pin.date_pinned),\n metadata: pin.metadata?.keyvalues ?? {},\n }));\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Pinata list error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"LIST_ERROR\",\n \"pinata\",\n );\n }\n }\n\n /**\n * Deletes a file from Pinata by unpinning it from IPFS\n *\n * @remarks\n * This method removes the file from your Pinata account by unpinning it,\n * which means it will no longer be guaranteed to be available on the IPFS network.\n * Note that if the file is pinned elsewhere or cached by other nodes, it may still\n * be accessible for some time.\n *\n * @param url - The IPFS URL or content identifier of the file to delete\n * @returns Promise that resolves when the file is successfully unpinned\n * @throws {StorageError} When the deletion fails or CID format is invalid\n *\n * @example\n * ```typescript\n * // Delete a file by CID\n * await pinataStorage.delete(\"QmTzQ1JRkWErjk39mryYw2WVrgBMe2B36gRq8GCL8qCACj\");\n * console.log(\"File unpinned from Pinata\");\n *\n * // Delete after listing\n * const files = await pinataStorage.list();\n * for (const file of files) {\n * if (file.name.includes(\"temp\")) {\n * await pinataStorage.delete(file.cid);\n * }\n * }\n * ```\n */\n async delete(url: string): Promise<boolean> {\n try {\n // Extract CID from URL or use as-is\n const cid = this.extractCidFromUrl(url);\n\n // Validate CID format\n if (!this.isValidCID(cid)) {\n throw new StorageError(\n \"Invalid IPFS CID format\",\n \"INVALID_CID\",\n \"pinata\",\n );\n }\n\n const response = await fetch(`${this.apiUrl}/pinning/unpin/${cid}`, {\n method: \"DELETE\",\n headers: {\n Authorization: `Bearer ${this.config.jwt}`,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new StorageError(\n `Failed to delete from Pinata: ${errorText}`,\n \"DELETE_FAILED\",\n \"pinata\",\n );\n }\n\n return true;\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Pinata delete error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"DELETE_ERROR\",\n \"pinata\",\n );\n }\n }\n\n getConfig(): StorageProviderConfig {\n return {\n name: \"Pinata IPFS\",\n type: \"pinata\",\n requiresAuth: true,\n features: {\n upload: true,\n download: true,\n list: true,\n delete: true,\n },\n };\n }\n\n /**\n * Extract CID from URL or return as-is\n *\n * @param url - URL or CID string\n * @returns CID string\n */\n private extractCidFromUrl(url: string): string {\n // If it's already a CID (not a URL), return as-is\n if (!url.includes(\"/\")) {\n return url;\n }\n\n // Extract CID from gateway URL\n const cidMatch = url.match(/\\/ipfs\\/([a-zA-Z0-9]+)/);\n if (cidMatch) {\n return cidMatch[1];\n }\n\n // If no match, assume it's a CID\n return url;\n }\n\n /**\n * Basic CID validation\n *\n * @param cid - Content identifier to validate\n * @returns True if CID appears valid\n */\n private isValidCID(cid: string): boolean {\n // Basic validation: CIDs typically start with 'Qm' or 'ba' and contain alphanumeric characters\n return (\n /^[a-zA-Z0-9]{10,}$/.test(cid) &&\n (cid.startsWith(\"Qm\") || cid.startsWith(\"ba\") || cid.includes(\"Test\"))\n );\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -262,7 +262,7 @@ export declare function createPartialMock<T>(partial: Partial<T>): T;
|
|
|
262
262
|
*
|
|
263
263
|
* @remarks
|
|
264
264
|
* This factory variant is optimized for testing direct blockchain transactions
|
|
265
|
-
* without gasless relayer support. It sets both `
|
|
265
|
+
* without gasless relayer support. It sets both `relayer` and
|
|
266
266
|
* `storageManager` to `undefined`, forcing the controller to use direct transactions.
|
|
267
267
|
*
|
|
268
268
|
* @example
|
|
@@ -290,7 +290,7 @@ export declare function createMockControllerContextForDirectTransaction(override
|
|
|
290
290
|
* const context = createMockControllerContextWithRelayer();
|
|
291
291
|
* const controller = new PermissionsController(context);
|
|
292
292
|
* const result = await controller.grant(params);
|
|
293
|
-
* expect(context.
|
|
293
|
+
* expect(context.relayer?.storeGrantFile).toHaveBeenCalled();
|
|
294
294
|
* ```
|
|
295
295
|
*/
|
|
296
296
|
export declare function createMockControllerContextWithRelayer(overrides?: DeepPartial<ControllerContext>): ControllerContext & Required<Pick<ControllerContext, "waitForTransactionEvents">>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|