@opendatalabs/vana-sdk 0.1.0-alpha.2b6935d → 0.1.0-alpha.2e77fcc
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/base.cjs +33 -0
- package/dist/controllers/base.cjs.map +1 -1
- package/dist/controllers/base.d.ts +10 -0
- package/dist/controllers/base.js +33 -0
- package/dist/controllers/base.js.map +1 -1
- package/dist/controllers/data.cjs +162 -133
- package/dist/controllers/data.cjs.map +1 -1
- package/dist/controllers/data.d.ts +222 -184
- package/dist/controllers/data.js +162 -133
- package/dist/controllers/data.js.map +1 -1
- package/dist/controllers/permissions.cjs +208 -60
- package/dist/controllers/permissions.cjs.map +1 -1
- package/dist/controllers/permissions.d.ts +75 -45
- package/dist/controllers/permissions.js +208 -60
- 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 +23 -21
- package/dist/controllers/schemas.cjs.map +1 -1
- package/dist/controllers/schemas.d.ts +47 -40
- package/dist/controllers/schemas.js +23 -21
- 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 +4 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.ts +2 -1
- package/dist/core.js +4 -1
- 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 +2 -3
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.d.ts +33 -13
- 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 +136 -98
- package/dist/server/relayerHandler.cjs.map +1 -1
- package/dist/server/relayerHandler.d.ts +3 -2
- package/dist/server/relayerHandler.js +135 -96
- package/dist/server/relayerHandler.js.map +1 -1
- 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/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 +38 -4
- 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 -1
- 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 +81 -10
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.d.ts +28 -2
- package/dist/types/index.js.map +1 -1
- package/dist/types/operations.cjs.map +1 -1
- package/dist/types/operations.d.ts +178 -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 +114 -18
- 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/types/utils.cjs.map +1 -1
- package/dist/types/utils.d.ts +5 -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/ipfs.cjs +2 -4
- package/dist/utils/ipfs.cjs.map +1 -1
- package/dist/utils/ipfs.d.ts +1 -1
- package/dist/utils/ipfs.js +2 -4
- package/dist/utils/ipfs.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.map +1 -1
- package/dist/utils/wallet.d.ts +78 -16
- package/dist/utils/wallet.js.map +1 -1
- package/package.json +3 -1
- package/dist/server/handler.cjs +0 -103
- package/dist/server/handler.cjs.map +0 -1
- package/dist/server/handler.d.ts +0 -95
- package/dist/server/handler.js +0 -79
- package/dist/server/handler.js.map +0 -1
- /package/dist/tests/{server-handler.test.d.ts → permissions-transaction-options.test.d.ts} +0 -0
|
@@ -1,46 +1,115 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from "uuid";
|
|
1
2
|
import { SignatureError } from "../errors";
|
|
2
3
|
import { recoverTypedDataAddress, getAddress } from "viem";
|
|
3
|
-
async function handleRelayerOperation(sdk, request) {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
async function handleRelayerOperation(sdk, request, options) {
|
|
5
|
+
const store = sdk.operationStore;
|
|
6
|
+
if (store) {
|
|
7
|
+
if (request.type === "status_check") {
|
|
8
|
+
const state = await store.get(request.operationId);
|
|
9
|
+
if (!state) return { type: "error", error: "Operation not found" };
|
|
10
|
+
if (state.status === "confirmed")
|
|
11
|
+
return {
|
|
12
|
+
type: "confirmed",
|
|
13
|
+
hash: state.transactionHash,
|
|
14
|
+
receipt: state.finalReceipt
|
|
15
|
+
};
|
|
16
|
+
if (state.status === "failed")
|
|
17
|
+
return { type: "error", error: state.error ?? "Operation failed" };
|
|
18
|
+
return { type: "pending", operationId: request.operationId };
|
|
19
|
+
}
|
|
20
|
+
const operationId = uuidv4();
|
|
21
|
+
try {
|
|
22
|
+
const txResult = await routeAndExecuteRequest(sdk, request, options);
|
|
23
|
+
if ("hash" in txResult) {
|
|
24
|
+
await store.set(operationId, {
|
|
25
|
+
status: "pending",
|
|
26
|
+
transactionHash: txResult.hash,
|
|
27
|
+
originalRequest: request,
|
|
28
|
+
nonce: options?.nonce,
|
|
29
|
+
retryCount: 0,
|
|
30
|
+
lastAttemptedGas: {
|
|
31
|
+
maxFeePerGas: options?.maxFeePerGas?.toString(),
|
|
32
|
+
maxPriorityFeePerGas: options?.maxPriorityFeePerGas?.toString()
|
|
33
|
+
},
|
|
34
|
+
submittedAt: Date.now()
|
|
35
|
+
});
|
|
36
|
+
return { type: "pending", operationId };
|
|
37
|
+
} else {
|
|
38
|
+
return { type: "direct_result_untracked", result: txResult };
|
|
39
|
+
}
|
|
40
|
+
} catch (e) {
|
|
41
|
+
const error = e instanceof Error ? e : new Error("Unknown error during operation submission");
|
|
42
|
+
return { type: "error", error: error.message };
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
if (request.type === "status_check") {
|
|
46
|
+
return {
|
|
47
|
+
type: "error",
|
|
48
|
+
error: "Stateless relayer does not support operation status checks."
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const txResult = await routeAndExecuteRequest(sdk, request, options);
|
|
53
|
+
if ("hash" in txResult) {
|
|
54
|
+
return { type: "submitted", hash: txResult.hash };
|
|
55
|
+
} else {
|
|
56
|
+
return { type: "direct_result_untracked", result: txResult };
|
|
57
|
+
}
|
|
58
|
+
} catch (e) {
|
|
59
|
+
const error = e instanceof Error ? e : new Error("Unknown error during operation submission");
|
|
60
|
+
return { type: "error", error: error.message };
|
|
61
|
+
}
|
|
6
62
|
}
|
|
7
|
-
return handleDirectOperation(sdk, request);
|
|
8
63
|
}
|
|
9
|
-
async function
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
`Signature verification failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
if (expectedUserAddress) {
|
|
29
|
-
const normalizedExpected = getAddress(expectedUserAddress);
|
|
30
|
-
const normalizedSigner = getAddress(recoveredAddress);
|
|
31
|
-
if (normalizedSigner !== normalizedExpected) {
|
|
64
|
+
async function routeAndExecuteRequest(sdk, request, options) {
|
|
65
|
+
if (request.type === "signed") {
|
|
66
|
+
const { typedData, signature, expectedUserAddress } = request;
|
|
67
|
+
let recoveredAddress;
|
|
68
|
+
try {
|
|
69
|
+
recoveredAddress = await recoverTypedDataAddress({
|
|
70
|
+
domain: {
|
|
71
|
+
...typedData.domain,
|
|
72
|
+
chainId: typedData.domain.chainId ? BigInt(typedData.domain.chainId) : void 0
|
|
73
|
+
},
|
|
74
|
+
types: typedData.types,
|
|
75
|
+
primaryType: typedData.primaryType,
|
|
76
|
+
message: typedData.message,
|
|
77
|
+
signature
|
|
78
|
+
});
|
|
79
|
+
} catch (error) {
|
|
32
80
|
throw new SignatureError(
|
|
33
|
-
`
|
|
81
|
+
`Signature verification failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
34
82
|
);
|
|
35
83
|
}
|
|
84
|
+
if (expectedUserAddress) {
|
|
85
|
+
const normalizedExpected = getAddress(expectedUserAddress);
|
|
86
|
+
const normalizedSigner = getAddress(recoveredAddress);
|
|
87
|
+
if (normalizedSigner !== normalizedExpected) {
|
|
88
|
+
throw new SignatureError(
|
|
89
|
+
`Security verification failed: Recovered signer address (${normalizedSigner}) does not match expected user address (${normalizedExpected})`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return await routeSignedOperation(sdk, typedData, signature, options);
|
|
94
|
+
} else if (request.type === "direct") {
|
|
95
|
+
return await handleDirectOperation(sdk, request, options);
|
|
36
96
|
}
|
|
37
|
-
|
|
38
|
-
return {
|
|
39
|
-
type: "signed",
|
|
40
|
-
hash: result.hash
|
|
41
|
-
};
|
|
97
|
+
throw new Error("Invalid request type for execution");
|
|
42
98
|
}
|
|
43
|
-
async function routeSignedOperation(sdk, typedData, signature) {
|
|
99
|
+
async function routeSignedOperation(sdk, typedData, signature, options) {
|
|
100
|
+
const validPrimaryTypes = [
|
|
101
|
+
"Permission",
|
|
102
|
+
"RevokePermission",
|
|
103
|
+
"TrustServer",
|
|
104
|
+
"AddServer",
|
|
105
|
+
"UntrustServer",
|
|
106
|
+
"ServerFilesAndPermission"
|
|
107
|
+
];
|
|
108
|
+
if (!validPrimaryTypes.includes(typedData.primaryType)) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
`Unsupported operation type: ${typedData.primaryType}. Supported types: ${validPrimaryTypes.join(", ")}`
|
|
111
|
+
);
|
|
112
|
+
}
|
|
44
113
|
const primaryType = typedData.primaryType;
|
|
45
114
|
switch (primaryType) {
|
|
46
115
|
case "Permission":
|
|
@@ -49,7 +118,8 @@ async function routeSignedOperation(sdk, typedData, signature) {
|
|
|
49
118
|
...typedData,
|
|
50
119
|
primaryType: "Permission"
|
|
51
120
|
},
|
|
52
|
-
signature
|
|
121
|
+
signature,
|
|
122
|
+
options
|
|
53
123
|
);
|
|
54
124
|
case "RevokePermission":
|
|
55
125
|
return sdk.permissions.submitSignedRevoke(
|
|
@@ -57,7 +127,8 @@ async function routeSignedOperation(sdk, typedData, signature) {
|
|
|
57
127
|
...typedData,
|
|
58
128
|
primaryType: "RevokePermission"
|
|
59
129
|
},
|
|
60
|
-
signature
|
|
130
|
+
signature,
|
|
131
|
+
options
|
|
61
132
|
);
|
|
62
133
|
case "TrustServer":
|
|
63
134
|
return sdk.permissions.submitSignedTrustServer(
|
|
@@ -65,7 +136,8 @@ async function routeSignedOperation(sdk, typedData, signature) {
|
|
|
65
136
|
...typedData,
|
|
66
137
|
primaryType: "TrustServer"
|
|
67
138
|
},
|
|
68
|
-
signature
|
|
139
|
+
signature,
|
|
140
|
+
options
|
|
69
141
|
);
|
|
70
142
|
case "AddServer":
|
|
71
143
|
return sdk.permissions.submitSignedAddAndTrustServer(
|
|
@@ -73,7 +145,8 @@ async function routeSignedOperation(sdk, typedData, signature) {
|
|
|
73
145
|
...typedData,
|
|
74
146
|
primaryType: "AddServer"
|
|
75
147
|
},
|
|
76
|
-
signature
|
|
148
|
+
signature,
|
|
149
|
+
options
|
|
77
150
|
);
|
|
78
151
|
case "UntrustServer":
|
|
79
152
|
return sdk.permissions.submitSignedUntrustServer(
|
|
@@ -81,7 +154,8 @@ async function routeSignedOperation(sdk, typedData, signature) {
|
|
|
81
154
|
...typedData,
|
|
82
155
|
primaryType: "UntrustServer"
|
|
83
156
|
},
|
|
84
|
-
signature
|
|
157
|
+
signature,
|
|
158
|
+
options
|
|
85
159
|
);
|
|
86
160
|
case "ServerFilesAndPermission":
|
|
87
161
|
return sdk.permissions.submitSignedAddServerFilesAndPermissions(
|
|
@@ -89,83 +163,53 @@ async function routeSignedOperation(sdk, typedData, signature) {
|
|
|
89
163
|
...typedData,
|
|
90
164
|
primaryType: "ServerFilesAndPermission"
|
|
91
165
|
},
|
|
92
|
-
signature
|
|
166
|
+
signature,
|
|
167
|
+
options
|
|
93
168
|
);
|
|
94
|
-
// RegisterGrantee
|
|
169
|
+
// TODO: RegisterGrantee with signature is not supported until
|
|
170
|
+
// DataPortabilityGrantees contract adds registerGranteeWithSignature function
|
|
95
171
|
// case "RegisterGrantee":
|
|
96
172
|
// return sdk.permissions.submitSignedRegisterGrantee(...);
|
|
97
173
|
default:
|
|
98
|
-
throw new Error(
|
|
174
|
+
throw new Error(
|
|
175
|
+
`Unsupported operation type: ${typedData.primaryType}. Supported types: Permission, RevokePermission, TrustServer, AddServer, UntrustServer, ServerFilesAndPermission`
|
|
176
|
+
);
|
|
99
177
|
}
|
|
100
178
|
}
|
|
101
|
-
async function handleDirectOperation(sdk, request) {
|
|
179
|
+
async function handleDirectOperation(sdk, request, options) {
|
|
102
180
|
switch (request.operation) {
|
|
103
181
|
case "submitFileAddition": {
|
|
104
182
|
const { url, userAddress } = request.params;
|
|
105
183
|
const result = await sdk.data.addFileWithPermissions(
|
|
106
184
|
url,
|
|
107
185
|
userAddress,
|
|
108
|
-
[]
|
|
186
|
+
[],
|
|
109
187
|
// No permissions
|
|
188
|
+
options
|
|
110
189
|
);
|
|
111
|
-
|
|
112
|
-
const fileId = eventData.expectedEvents?.FileAdded?.fileId;
|
|
113
|
-
if (!fileId) {
|
|
114
|
-
throw new Error("Failed to get fileId from transaction events");
|
|
115
|
-
}
|
|
116
|
-
return {
|
|
117
|
-
type: "direct",
|
|
118
|
-
result: {
|
|
119
|
-
fileId: Number(fileId),
|
|
120
|
-
transactionHash: result.hash
|
|
121
|
-
}
|
|
122
|
-
};
|
|
190
|
+
return result;
|
|
123
191
|
}
|
|
124
192
|
case "submitFileAdditionWithPermissions": {
|
|
125
193
|
const { url, userAddress, permissions } = request.params;
|
|
126
194
|
const result = await sdk.data.addFileWithPermissions(
|
|
127
195
|
url,
|
|
128
196
|
userAddress,
|
|
129
|
-
permissions
|
|
197
|
+
permissions,
|
|
198
|
+
options
|
|
130
199
|
);
|
|
131
|
-
|
|
132
|
-
const fileId = eventData.expectedEvents?.FileAdded?.fileId;
|
|
133
|
-
if (!fileId) {
|
|
134
|
-
throw new Error("Failed to get fileId from transaction events");
|
|
135
|
-
}
|
|
136
|
-
return {
|
|
137
|
-
type: "direct",
|
|
138
|
-
result: {
|
|
139
|
-
fileId: Number(fileId),
|
|
140
|
-
transactionHash: result.hash
|
|
141
|
-
}
|
|
142
|
-
};
|
|
200
|
+
return result;
|
|
143
201
|
}
|
|
144
202
|
case "submitFileAdditionComplete": {
|
|
145
203
|
const { url, userAddress, permissions, schemaId, ownerAddress } = request.params;
|
|
146
|
-
const
|
|
147
|
-
account: p.account,
|
|
148
|
-
publicKey: p.key
|
|
149
|
-
// Map 'key' to 'publicKey'
|
|
150
|
-
}));
|
|
151
|
-
const result = await sdk.data.addFileWithPermissionsAndSchema(
|
|
204
|
+
const result = await sdk.data.addFileWithEncryptedPermissionsAndSchema(
|
|
152
205
|
url,
|
|
153
206
|
ownerAddress ?? userAddress,
|
|
154
|
-
|
|
155
|
-
|
|
207
|
+
permissions,
|
|
208
|
+
// Already in correct format with encrypted 'key' field
|
|
209
|
+
schemaId,
|
|
210
|
+
options
|
|
156
211
|
);
|
|
157
|
-
|
|
158
|
-
const fileId = eventData.expectedEvents?.FileAdded?.fileId;
|
|
159
|
-
if (!fileId) {
|
|
160
|
-
throw new Error("Failed to get fileId from transaction events");
|
|
161
|
-
}
|
|
162
|
-
return {
|
|
163
|
-
type: "direct",
|
|
164
|
-
result: {
|
|
165
|
-
fileId: Number(fileId),
|
|
166
|
-
transactionHash: result.hash
|
|
167
|
-
}
|
|
168
|
-
};
|
|
212
|
+
return result;
|
|
169
213
|
}
|
|
170
214
|
case "storeGrantFile": {
|
|
171
215
|
const grantFile = request.params;
|
|
@@ -176,10 +220,7 @@ async function handleDirectOperation(sdk, request) {
|
|
|
176
220
|
content: blob,
|
|
177
221
|
filename: `grant-${Date.now()}.json`
|
|
178
222
|
});
|
|
179
|
-
return {
|
|
180
|
-
type: "direct",
|
|
181
|
-
result: { url: result.url }
|
|
182
|
-
};
|
|
223
|
+
return { url: result.url };
|
|
183
224
|
}
|
|
184
225
|
default: {
|
|
185
226
|
const exhaustiveCheck = request;
|
|
@@ -187,9 +228,7 @@ async function handleDirectOperation(sdk, request) {
|
|
|
187
228
|
}
|
|
188
229
|
}
|
|
189
230
|
}
|
|
190
|
-
import { handleRelayerRequest } from "./handler";
|
|
191
231
|
export {
|
|
192
|
-
handleRelayerOperation
|
|
193
|
-
handleRelayerRequest
|
|
232
|
+
handleRelayerOperation
|
|
194
233
|
};
|
|
195
234
|
//# sourceMappingURL=relayerHandler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/server/relayerHandler.ts"],"sourcesContent":["import type { VanaInstance } from \"../index.node\";\nimport type {\n UnifiedRelayerRequest,\n UnifiedRelayerResponse,\n SignedRelayerRequest,\n DirectRelayerRequest,\n} from \"../types/relayer\";\nimport type {\n GenericTypedData,\n PermissionGrantTypedData,\n RevokePermissionTypedData,\n TrustServerTypedData,\n AddAndTrustServerTypedData,\n ServerFilesAndPermissionTypedData,\n TypedDataPrimaryType,\n} from \"../types/permissions\";\nimport { SignatureError } from \"../errors\";\nimport { recoverTypedDataAddress, getAddress, type Hash } from \"viem\";\n\n/**\n * Universal handler for all relayer operations.\n *\n * This function processes both EIP-712 signed operations and direct operations,\n * automatically routing to the appropriate SDK methods.\n *\n * @param sdk - Initialized Vana SDK instance\n * @param request - The unified relayer request\n * @returns Promise resolving to operation-specific response\n *\n * @category Server\n * @example\n * ```typescript\n * // In your server endpoint (Next.js example):\n * import { handleRelayerOperation } from '@opendatalabs/vana-sdk/node';\n *\n * export async function POST(request: NextRequest) {\n * try {\n * const body = await request.json();\n * const vana = getServerVanaInstance(); // Your server SDK instance\n *\n * const result = await handleRelayerOperation(vana, body);\n *\n * return NextResponse.json(result);\n * } catch (error) {\n * return NextResponse.json(\n * { error: error.message },\n * { status: 500 }\n * );\n * }\n * }\n * ```\n */\nexport async function handleRelayerOperation(\n sdk: VanaInstance,\n request: UnifiedRelayerRequest,\n): Promise<UnifiedRelayerResponse> {\n // Handle signed operations (EIP-712)\n if (request.type === \"signed\") {\n return handleSignedOperation(sdk, request);\n }\n\n // Handle direct operations (non-signed)\n return handleDirectOperation(sdk, request);\n}\n\n/**\n * Handle EIP-712 signed operations with full type safety\n */\nasync function handleSignedOperation(\n sdk: VanaInstance,\n request: SignedRelayerRequest,\n): Promise<UnifiedRelayerResponse> {\n const { typedData, signature, expectedUserAddress } = request;\n\n // Step 1: Verify the signature (security check)\n let recoveredAddress: `0x${string}`;\n try {\n recoveredAddress = await recoverTypedDataAddress({\n domain: {\n ...typedData.domain,\n chainId: typedData.domain.chainId\n ? BigInt(typedData.domain.chainId)\n : undefined,\n },\n types: typedData.types,\n primaryType: typedData.primaryType,\n message: typedData.message as unknown as Record<string, unknown>,\n signature,\n });\n } catch (error) {\n // Handle signature verification errors\n throw new SignatureError(\n `Signature verification failed: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n\n // Optional security check: Verify the signer matches expected address\n if (expectedUserAddress) {\n const normalizedExpected = getAddress(expectedUserAddress);\n const normalizedSigner = getAddress(recoveredAddress);\n\n if (normalizedSigner !== normalizedExpected) {\n throw new SignatureError(\n `Security verification failed: Recovered signer address (${normalizedSigner}) does not match expected user address (${normalizedExpected})`,\n );\n }\n }\n\n // Step 2: Route to appropriate SDK method based on primaryType\n // Using proper type narrowing instead of unsafe casts\n const result = await routeSignedOperation(sdk, typedData, signature);\n\n // Return the transaction hash with type\n return {\n type: \"signed\",\n hash: result.hash,\n };\n}\n\n/**\n * Route signed operations to the appropriate SDK method with type safety\n */\nasync function routeSignedOperation(\n sdk: VanaInstance,\n typedData: GenericTypedData,\n signature: Hash,\n) {\n const primaryType = typedData.primaryType as TypedDataPrimaryType;\n\n // Type-safe routing based on primaryType\n switch (primaryType) {\n case \"Permission\":\n // TypeScript knows this is a Permission operation\n return sdk.permissions.submitSignedGrant(\n {\n ...typedData,\n primaryType: \"Permission\",\n } as PermissionGrantTypedData,\n signature,\n );\n\n case \"RevokePermission\":\n return sdk.permissions.submitSignedRevoke(\n {\n ...typedData,\n primaryType: \"RevokePermission\",\n } as RevokePermissionTypedData,\n signature,\n );\n\n case \"TrustServer\":\n return sdk.permissions.submitSignedTrustServer(\n {\n ...typedData,\n primaryType: \"TrustServer\",\n } as TrustServerTypedData,\n signature,\n );\n\n case \"AddServer\":\n return sdk.permissions.submitSignedAddAndTrustServer(\n {\n ...typedData,\n primaryType: \"AddServer\",\n } as AddAndTrustServerTypedData,\n signature,\n );\n\n case \"UntrustServer\":\n return sdk.permissions.submitSignedUntrustServer(\n {\n ...typedData,\n primaryType: \"UntrustServer\",\n } as GenericTypedData,\n signature,\n );\n\n case \"ServerFilesAndPermission\":\n return sdk.permissions.submitSignedAddServerFilesAndPermissions(\n {\n ...typedData,\n primaryType: \"ServerFilesAndPermission\",\n } as ServerFilesAndPermissionTypedData,\n signature,\n );\n\n // RegisterGrantee is commented out as it's not supported by smart contracts yet\n // case \"RegisterGrantee\":\n // return sdk.permissions.submitSignedRegisterGrantee(...);\n\n default:\n throw new Error(`Unsupported operation type: ${typedData.primaryType}`);\n }\n}\n\n/**\n * Handle direct (non-signed) operations\n */\nasync function handleDirectOperation(\n sdk: VanaInstance,\n request: DirectRelayerRequest,\n): Promise<UnifiedRelayerResponse> {\n switch (request.operation) {\n case \"submitFileAddition\": {\n const { url, userAddress } = request.params;\n\n // Use SDK to add file with no permissions\n const result = await sdk.data.addFileWithPermissions(\n url,\n userAddress,\n [], // No permissions\n );\n\n // Wait for transaction events to get fileId\n const eventData = await sdk.waitForTransactionEvents(result);\n const fileId = eventData.expectedEvents?.FileAdded?.fileId;\n\n if (!fileId) {\n throw new Error(\"Failed to get fileId from transaction events\");\n }\n\n return {\n type: \"direct\",\n result: {\n fileId: Number(fileId),\n transactionHash: result.hash,\n },\n };\n }\n\n case \"submitFileAdditionWithPermissions\": {\n const { url, userAddress, permissions } = request.params;\n\n // Use SDK to add file with permissions\n const result = await sdk.data.addFileWithPermissions(\n url,\n userAddress,\n permissions,\n );\n\n // Wait for transaction events to get fileId\n const eventData = await sdk.waitForTransactionEvents(result);\n const fileId = eventData.expectedEvents?.FileAdded?.fileId;\n\n if (!fileId) {\n throw new Error(\"Failed to get fileId from transaction events\");\n }\n\n return {\n type: \"direct\",\n result: {\n fileId: Number(fileId),\n transactionHash: result.hash,\n },\n };\n }\n\n case \"submitFileAdditionComplete\": {\n const { url, userAddress, permissions, schemaId, ownerAddress } =\n request.params;\n\n // Map permissions from relayer format to SDK format\n const sdkPermissions = permissions.map((p) => ({\n account: p.account,\n publicKey: p.key, // Map 'key' to 'publicKey'\n }));\n\n // Use SDK to add file with permissions and schema\n const result = await sdk.data.addFileWithPermissionsAndSchema(\n url,\n ownerAddress ?? userAddress,\n sdkPermissions,\n schemaId,\n );\n\n // Wait for transaction events to get fileId\n const eventData = await sdk.waitForTransactionEvents(result);\n const fileId = eventData.expectedEvents?.FileAdded?.fileId;\n\n if (!fileId) {\n throw new Error(\"Failed to get fileId from transaction events\");\n }\n\n return {\n type: \"direct\",\n result: {\n fileId: Number(fileId),\n transactionHash: result.hash,\n },\n };\n }\n\n case \"storeGrantFile\": {\n const grantFile = request.params;\n\n // Store grant file using SDK's data controller\n // Convert grant file to blob for storage\n const blob = new Blob([JSON.stringify(grantFile)], {\n type: \"application/json\",\n });\n\n // Upload using the data controller\n const result = await sdk.data.upload({\n content: blob,\n filename: `grant-${Date.now()}.json`,\n });\n\n return {\n type: \"direct\",\n result: { url: result.url },\n };\n }\n\n default: {\n // TypeScript exhaustiveness check - ensures all cases are handled at compile time\n const exhaustiveCheck: never = request;\n // Return exhaustiveCheck to satisfy TypeScript while throwing an error\n // This should never be reached if all cases are handled\n return exhaustiveCheck;\n }\n }\n}\n\n// Legacy handler export - DEPRECATED\n// TODO: Remove in next major version\n// Migration guide: Use handleRelayerOperation instead\nexport { handleRelayerRequest } from \"./handler\";\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/server/relayerHandler.ts"],"sourcesContent":["import { v4 as uuidv4 } from \"uuid\";\nimport type { VanaInstance } from \"../index.node\";\nimport type {\n UnifiedRelayerRequest,\n UnifiedRelayerResponse,\n DirectRelayerRequest,\n} from \"../types/relayer\";\nimport type {\n TransactionResult,\n TransactionOptions,\n IOperationStore,\n} from \"../types\";\nimport type { Contract, Fn } from \"../generated/event-types\";\nimport type {\n GenericTypedData,\n PermissionGrantTypedData,\n RevokePermissionTypedData,\n TrustServerTypedData,\n AddAndTrustServerTypedData,\n ServerFilesAndPermissionTypedData,\n TypedDataPrimaryType,\n} from \"../types/permissions\";\nimport { SignatureError } from \"../errors\";\nimport { recoverTypedDataAddress, getAddress, type Hash } from \"viem\";\n\n/**\n * Universal handler for all relayer operations.\n *\n * This function processes both EIP-712 signed operations and direct operations,\n * automatically routing to the appropriate SDK methods.\n *\n * @param sdk - Initialized Vana SDK instance\n * @param request - The unified relayer request\n * @returns Promise resolving to operation-specific response\n *\n * @category Server\n * @example\n * ```typescript\n * // In your server endpoint (Next.js example):\n * import { handleRelayerOperation } from '@opendatalabs/vana-sdk/node';\n *\n * export async function POST(request: NextRequest) {\n * try {\n * const body = await request.json();\n * const vana = getServerVanaInstance(); // Your server SDK instance\n *\n * const result = await handleRelayerOperation(vana, body);\n *\n * return NextResponse.json(result);\n * } catch (error) {\n * return NextResponse.json(\n * { error: error.message },\n * { status: 500 }\n * );\n * }\n * }\n * ```\n */\nexport async function handleRelayerOperation(\n sdk: VanaInstance, // The public signature is generic\n request: UnifiedRelayerRequest,\n options?: TransactionOptions,\n): Promise<UnifiedRelayerResponse> {\n // Type assertion required: VanaInstance public interface doesn't expose operationStore\n // to maintain API simplicity, but server-side handler needs access for stateful mode.\n // The factory overloads guarantee this is safe for relayer-configured instances.\n // TODO(TYPES): Consider exposing operationStore through a server-specific interface\n\n const store = (sdk as any).operationStore as IOperationStore | undefined;\n\n // --- STATEFUL (ROBUST) MODE ---\n // This block executes ONLY if the developer provided an IOperationStore.\n if (store) {\n if (request.type === \"status_check\") {\n const state = await store.get(request.operationId);\n if (!state) return { type: \"error\", error: \"Operation not found\" };\n if (state.status === \"confirmed\")\n return {\n type: \"confirmed\",\n hash: state.transactionHash,\n receipt: state.finalReceipt,\n };\n if (state.status === \"failed\")\n return { type: \"error\", error: state.error ?? \"Operation failed\" };\n return { type: \"pending\", operationId: request.operationId };\n }\n\n const operationId = uuidv4();\n try {\n const txResult = await routeAndExecuteRequest(sdk, request, options);\n\n // We only store state for operations that result in a transaction.\n if (\"hash\" in txResult) {\n await store.set(operationId, {\n status: \"pending\",\n transactionHash: txResult.hash,\n originalRequest: request,\n nonce: options?.nonce,\n retryCount: 0,\n lastAttemptedGas: {\n maxFeePerGas: options?.maxFeePerGas?.toString(),\n maxPriorityFeePerGas: options?.maxPriorityFeePerGas?.toString(),\n },\n submittedAt: Date.now(),\n });\n return { type: \"pending\", operationId };\n } else {\n // This handles non-transactional direct operations like `storeGrantFile`\n return { type: \"direct_result_untracked\", result: txResult };\n }\n } catch (e) {\n const error =\n e instanceof Error\n ? e\n : new Error(\"Unknown error during operation submission\");\n // We don't create a persistent error state for an initial submission failure.\n return { type: \"error\", error: error.message };\n }\n }\n\n // --- STATELESS (SIMPLE) MODE ---\n // This block executes if no IOperationStore was provided. Fire-and-forget.\n else {\n if (request.type === \"status_check\") {\n return {\n type: \"error\",\n error: \"Stateless relayer does not support operation status checks.\",\n };\n }\n\n try {\n const txResult = await routeAndExecuteRequest(sdk, request, options);\n\n // For stateless relayers, we can only handle transactional results.\n if (\"hash\" in txResult) {\n return { type: \"submitted\", hash: txResult.hash };\n } else {\n // Non-transactional direct operations can return their result directly.\n return { type: \"direct_result_untracked\", result: txResult };\n }\n } catch (e) {\n const error =\n e instanceof Error\n ? e\n : new Error(\"Unknown error during operation submission\");\n return { type: \"error\", error: error.message };\n }\n }\n}\n\n/**\n * Helper function to route and execute requests.\n * This simply delegates to the appropriate handler based on request type.\n */\nasync function routeAndExecuteRequest(\n sdk: VanaInstance,\n request: UnifiedRelayerRequest,\n options?: TransactionOptions,\n): Promise<TransactionResult<Contract, Fn<Contract>> | { url: string }> {\n if (request.type === \"signed\") {\n const { typedData, signature, expectedUserAddress } = request;\n\n // Step 1: Verify the signature (security check)\n let recoveredAddress: `0x${string}`;\n try {\n recoveredAddress = await recoverTypedDataAddress({\n domain: {\n ...typedData.domain,\n chainId: typedData.domain.chainId\n ? BigInt(typedData.domain.chainId)\n : undefined,\n },\n types: typedData.types,\n primaryType: typedData.primaryType,\n message: typedData.message as unknown as Record<string, unknown>,\n signature,\n });\n } catch (error) {\n // Handle signature verification errors\n throw new SignatureError(\n `Signature verification failed: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n\n // Optional security check: Verify the signer matches expected address\n if (expectedUserAddress) {\n const normalizedExpected = getAddress(expectedUserAddress);\n const normalizedSigner = getAddress(recoveredAddress);\n\n if (normalizedSigner !== normalizedExpected) {\n throw new SignatureError(\n `Security verification failed: Recovered signer address (${normalizedSigner}) does not match expected user address (${normalizedExpected})`,\n );\n }\n }\n\n // Step 2: Route to appropriate SDK method based on primaryType\n return await routeSignedOperation(sdk, typedData, signature, options);\n } else if (request.type === \"direct\") {\n return await handleDirectOperation(sdk, request, options);\n }\n throw new Error(\"Invalid request type for execution\");\n}\n\n/**\n * Route signed operations to the appropriate SDK method with type safety.\n *\n * Returns a TransactionResult for one of the valid contracts and their functions.\n * Using Contract and Fn<Contract> ensures we're working with known contract types.\n */\nasync function routeSignedOperation(\n sdk: VanaInstance,\n typedData: GenericTypedData,\n signature: Hash,\n options?: TransactionOptions,\n): Promise<TransactionResult<Contract, Fn<Contract>>> {\n // Validate primaryType before casting\n const validPrimaryTypes: readonly TypedDataPrimaryType[] = [\n \"Permission\",\n \"RevokePermission\",\n \"TrustServer\",\n \"AddServer\",\n \"UntrustServer\",\n \"ServerFilesAndPermission\",\n ] as const;\n\n if (\n !validPrimaryTypes.includes(typedData.primaryType as TypedDataPrimaryType)\n ) {\n throw new Error(\n `Unsupported operation type: ${typedData.primaryType}. ` +\n `Supported types: ${validPrimaryTypes.join(\", \")}`,\n );\n }\n\n const primaryType = typedData.primaryType as TypedDataPrimaryType;\n\n // Type-safe routing based on primaryType\n switch (primaryType) {\n case \"Permission\":\n // TypeScript knows this is a Permission operation\n return sdk.permissions.submitSignedGrant(\n {\n ...typedData,\n primaryType: \"Permission\",\n } as PermissionGrantTypedData,\n signature,\n options,\n );\n\n case \"RevokePermission\":\n return sdk.permissions.submitSignedRevoke(\n {\n ...typedData,\n primaryType: \"RevokePermission\",\n } as RevokePermissionTypedData,\n signature,\n options,\n );\n\n case \"TrustServer\":\n // Note: TrustServer operation is deprecated but still supported for backwards compatibility\n // New implementations should use AddServer (AddAndTrustServer) instead\n return sdk.permissions.submitSignedTrustServer(\n {\n ...typedData,\n primaryType: \"TrustServer\",\n } as TrustServerTypedData,\n signature,\n options,\n );\n\n case \"AddServer\":\n return sdk.permissions.submitSignedAddAndTrustServer(\n {\n ...typedData,\n primaryType: \"AddServer\",\n } as AddAndTrustServerTypedData,\n signature,\n options,\n );\n\n case \"UntrustServer\":\n return sdk.permissions.submitSignedUntrustServer(\n {\n ...typedData,\n primaryType: \"UntrustServer\",\n } as GenericTypedData,\n signature,\n options,\n );\n\n case \"ServerFilesAndPermission\":\n return sdk.permissions.submitSignedAddServerFilesAndPermissions(\n {\n ...typedData,\n primaryType: \"ServerFilesAndPermission\",\n } as ServerFilesAndPermissionTypedData,\n signature,\n options,\n );\n\n // TODO: RegisterGrantee with signature is not supported until\n // DataPortabilityGrantees contract adds registerGranteeWithSignature function\n // case \"RegisterGrantee\":\n // return sdk.permissions.submitSignedRegisterGrantee(...);\n\n default:\n // This should never be reached due to validation above, but TypeScript requires it\n throw new Error(\n `Unsupported operation type: ${typedData.primaryType}. ` +\n `Supported types: Permission, RevokePermission, TrustServer, AddServer, UntrustServer, ServerFilesAndPermission`,\n );\n }\n}\n\n/**\n * Handle direct (non-signed) operations\n */\nasync function handleDirectOperation(\n sdk: VanaInstance,\n request: DirectRelayerRequest,\n options?: TransactionOptions,\n): Promise<TransactionResult<Contract, Fn<Contract>> | { url: string }> {\n switch (request.operation) {\n case \"submitFileAddition\": {\n const { url, userAddress } = request.params;\n\n // Use SDK to add file with no permissions\n const result = await sdk.data.addFileWithPermissions(\n url,\n userAddress,\n [], // No permissions\n options,\n );\n\n // Return the TransactionResult directly\n return result;\n }\n\n case \"submitFileAdditionWithPermissions\": {\n const { url, userAddress, permissions } = request.params;\n\n // Use SDK to add file with permissions\n const result = await sdk.data.addFileWithPermissions(\n url,\n userAddress,\n permissions,\n options,\n );\n\n // Return the TransactionResult directly\n return result;\n }\n\n case \"submitFileAdditionComplete\": {\n const { url, userAddress, permissions, schemaId, ownerAddress } =\n request.params;\n\n // Permissions are already encrypted, use the appropriate method\n // No mapping needed - permissions already have { account, key } format\n const result = await sdk.data.addFileWithEncryptedPermissionsAndSchema(\n url,\n ownerAddress ?? userAddress,\n permissions, // Already in correct format with encrypted 'key' field\n schemaId,\n options,\n );\n\n // Return the TransactionResult directly\n return result;\n }\n\n case \"storeGrantFile\": {\n const grantFile = request.params;\n\n // Store grant file using SDK's data controller\n // Convert grant file to blob for storage\n const blob = new Blob([JSON.stringify(grantFile)], {\n type: \"application/json\",\n });\n\n // Upload using the data controller\n const result = await sdk.data.upload({\n content: blob,\n filename: `grant-${Date.now()}.json`,\n });\n\n return { url: result.url };\n }\n\n default: {\n // TypeScript exhaustiveness check - ensures all cases are handled at compile time\n const exhaustiveCheck: never = request;\n // Return exhaustiveCheck to satisfy TypeScript while throwing an error\n // This should never be reached if all cases are handled\n return exhaustiveCheck;\n }\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/storage/manager.cjs
CHANGED
|
@@ -51,10 +51,20 @@ class StorageManager {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
|
-
*
|
|
54
|
+
* Retrieves a registered storage provider.
|
|
55
55
|
*
|
|
56
|
-
* @param name - Provider identifier
|
|
57
|
-
*
|
|
56
|
+
* @param name - Provider identifier.
|
|
57
|
+
* If not specified, returns the default provider.
|
|
58
|
+
* @returns The requested storage provider instance
|
|
59
|
+
*
|
|
60
|
+
* @throws {StorageError} With code 'NO_PROVIDER' if no provider available
|
|
61
|
+
* @throws {StorageError} With code 'PROVIDER_NOT_FOUND' if named provider doesn't exist
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* const provider = storage.getProvider('pinata');
|
|
66
|
+
* const config = provider.getConfig();
|
|
67
|
+
* ```
|
|
58
68
|
*/
|
|
59
69
|
getProvider(name) {
|
|
60
70
|
const providerName = name ?? this.defaultProvider;
|
|
@@ -76,25 +86,49 @@ class StorageManager {
|
|
|
76
86
|
return provider;
|
|
77
87
|
}
|
|
78
88
|
/**
|
|
79
|
-
*
|
|
89
|
+
* Lists all registered provider names.
|
|
90
|
+
*
|
|
91
|
+
* @returns Array of registered provider identifiers
|
|
80
92
|
*
|
|
81
|
-
* @
|
|
93
|
+
* @example
|
|
94
|
+
* ```typescript
|
|
95
|
+
* const providers = storage.listProviders();
|
|
96
|
+
* console.log('Available providers:', providers);
|
|
97
|
+
* // Output: ['ipfs', 'pinata', 'google-drive']
|
|
98
|
+
* ```
|
|
82
99
|
*/
|
|
83
100
|
listProviders() {
|
|
84
101
|
return Array.from(this.providers.keys());
|
|
85
102
|
}
|
|
86
103
|
/**
|
|
87
|
-
*
|
|
104
|
+
* Gets the current default provider name.
|
|
88
105
|
*
|
|
89
|
-
* @returns Default provider
|
|
106
|
+
* @returns Default provider identifier or null if none set
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* const defaultName = storage.getDefaultProvider();
|
|
111
|
+
* if (defaultName) {
|
|
112
|
+
* console.log(`Using ${defaultName} by default`);
|
|
113
|
+
* }
|
|
114
|
+
* ```
|
|
90
115
|
*/
|
|
91
116
|
getDefaultProvider() {
|
|
92
117
|
return this.defaultProvider;
|
|
93
118
|
}
|
|
94
119
|
/**
|
|
95
|
-
*
|
|
120
|
+
* Sets the default storage provider.
|
|
121
|
+
*
|
|
122
|
+
* @param name - Provider identifier to set as default.
|
|
123
|
+
* Must be a registered provider name.
|
|
96
124
|
*
|
|
97
|
-
* @
|
|
125
|
+
* @throws {StorageError} With code 'PROVIDER_NOT_FOUND' if provider not registered
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```typescript
|
|
129
|
+
* storage.setDefaultProvider('pinata');
|
|
130
|
+
* // Now all operations without provider name will use Pinata
|
|
131
|
+
* ```
|
|
98
132
|
*/
|
|
99
133
|
setDefaultProvider(name) {
|
|
100
134
|
if (!this.providers.has(name)) {
|
|
@@ -133,50 +167,99 @@ class StorageManager {
|
|
|
133
167
|
return provider.upload(file, filename);
|
|
134
168
|
}
|
|
135
169
|
/**
|
|
136
|
-
*
|
|
170
|
+
* Downloads a file from storage.
|
|
171
|
+
*
|
|
172
|
+
* @param url - The storage URL to download from.
|
|
173
|
+
* Format depends on the storage provider.
|
|
174
|
+
* @param providerName - Optional provider identifier.
|
|
175
|
+
* Uses default provider if not specified.
|
|
176
|
+
* @returns The downloaded file as a Blob
|
|
177
|
+
*
|
|
178
|
+
* @throws {StorageError} If download fails or provider unavailable
|
|
137
179
|
*
|
|
138
|
-
* @
|
|
139
|
-
*
|
|
140
|
-
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```typescript
|
|
182
|
+
* const blob = await storage.download('ipfs://QmXxx...');
|
|
183
|
+
* const text = await blob.text();
|
|
184
|
+
* ```
|
|
141
185
|
*/
|
|
142
186
|
async download(url, providerName) {
|
|
143
187
|
const provider = this.getProvider(providerName);
|
|
144
188
|
return provider.download(url);
|
|
145
189
|
}
|
|
146
190
|
/**
|
|
147
|
-
*
|
|
191
|
+
* Lists files in storage.
|
|
192
|
+
*
|
|
193
|
+
* @param options - Optional filtering and pagination.
|
|
194
|
+
* @param options.namePattern - Pattern to filter files.
|
|
195
|
+
* @param options.limit - Maximum files to return.
|
|
196
|
+
* @param providerName - Optional provider identifier.
|
|
197
|
+
* Uses default provider if not specified.
|
|
198
|
+
* @returns Array of file metadata
|
|
199
|
+
*
|
|
200
|
+
* @throws {StorageError} If listing fails or not supported by provider
|
|
148
201
|
*
|
|
149
|
-
* @
|
|
150
|
-
*
|
|
151
|
-
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```typescript
|
|
204
|
+
* const files = await storage.list(
|
|
205
|
+
* { namePattern: '*.json', limit: 10 },
|
|
206
|
+
* 'google-drive'
|
|
207
|
+
* );
|
|
208
|
+
* ```
|
|
152
209
|
*/
|
|
153
210
|
async list(options, providerName) {
|
|
154
211
|
const provider = this.getProvider(providerName);
|
|
155
212
|
return provider.list(options);
|
|
156
213
|
}
|
|
157
214
|
/**
|
|
158
|
-
*
|
|
215
|
+
* Deletes a file from storage.
|
|
159
216
|
*
|
|
160
|
-
* @param url - The storage URL
|
|
161
|
-
*
|
|
162
|
-
* @
|
|
217
|
+
* @param url - The storage URL to delete.
|
|
218
|
+
* Must be a valid URL for the provider.
|
|
219
|
+
* @param providerName - Optional provider identifier.
|
|
220
|
+
* Uses default provider if not specified.
|
|
221
|
+
* @returns True if deletion succeeded, false otherwise
|
|
222
|
+
*
|
|
223
|
+
* @throws {StorageError} If deletion fails or not supported by provider
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```typescript
|
|
227
|
+
* const success = await storage.delete('ipfs://QmXxx...');
|
|
228
|
+
* if (success) {
|
|
229
|
+
* console.log('File deleted successfully');
|
|
230
|
+
* }
|
|
231
|
+
* ```
|
|
163
232
|
*/
|
|
164
233
|
async delete(url, providerName) {
|
|
165
234
|
const provider = this.getProvider(providerName);
|
|
166
235
|
return provider.delete(url);
|
|
167
236
|
}
|
|
168
237
|
/**
|
|
169
|
-
*
|
|
238
|
+
* Gets all registered storage provider names.
|
|
170
239
|
*
|
|
171
|
-
* @returns Array of provider
|
|
240
|
+
* @returns Array of provider identifiers
|
|
241
|
+
*
|
|
242
|
+
* @deprecated Use `listProviders()` instead
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* ```typescript
|
|
246
|
+
* const providers = storage.getStorageProviders();
|
|
247
|
+
* ```
|
|
172
248
|
*/
|
|
173
249
|
getStorageProviders() {
|
|
174
250
|
return Array.from(this.providers.keys());
|
|
175
251
|
}
|
|
176
252
|
/**
|
|
177
|
-
*
|
|
253
|
+
* Gets the default storage provider name.
|
|
178
254
|
*
|
|
179
|
-
* @returns Default provider
|
|
255
|
+
* @returns Default provider identifier or undefined if none set
|
|
256
|
+
*
|
|
257
|
+
* @deprecated Use `getDefaultProvider()` instead
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* ```typescript
|
|
261
|
+
* const defaultProvider = storage.getDefaultStorageProvider();
|
|
262
|
+
* ```
|
|
180
263
|
*/
|
|
181
264
|
getDefaultStorageProvider() {
|
|
182
265
|
return this.defaultProvider ?? void 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/storage/manager.ts"],"sourcesContent":["import type {\n StorageProvider,\n StorageUploadResult,\n StorageFile,\n StorageListOptions,\n} from \"../types/storage\";\nimport { StorageError } from \"../types/storage\";\n\n/**\n * Manages multiple storage providers with a unified interface for file operations.\n *\n * @remarks\n * The StorageManager provides a consistent API for uploading, downloading, and managing\n * files across different storage backends including IPFS, Pinata, Google Drive, and\n * server-managed storage. It handles provider registration, default provider selection,\n * and automatic fallback scenarios for robust file operations.\n *\n * Used internally by DataController for encrypted file storage, but can also be used\n * directly for custom storage workflows. Each provider implements the `StorageProvider`\n * interface to ensure consistent behavior across different storage backends.\n *\n * The manager supports provider-specific configurations and features while maintaining\n * a uniform API surface for applications.\n * @example\n * ```typescript\n * import { StorageManager, IPFSStorage, PinataStorage } from '@opendatalabs/vana-sdk';\n *\n * const storage = new StorageManager();\n *\n * // Register multiple providers\n * storage.register('ipfs', new IPFSStorage({\n * apiEndpoint: 'https://api.pinata.cloud/pinning/pinFileToIPFS'\n * }), true);\n * storage.register('pinata', new PinataStorage({\n * jwt: 'your-pinata-jwt-token'\n * }));\n *\n * // Upload to default provider\n * const result = await storage.upload(fileBlob, 'myfile.json');\n *\n * // Upload to specific provider\n * const result2 = await storage.upload(fileBlob, 'myfile.json', 'pinata');\n * ```\n * @category Storage\n * @see {@link https://docs.vana.com/developer/vana-sdk-documentation/core-modules/storage-providers | Storage Providers Guide} for configuration details\n */\nexport class StorageManager {\n private providers: Map<string, StorageProvider> = new Map();\n private defaultProvider: string | null = null;\n\n /**\n * Registers a storage provider with the manager.\n *\n * @remarks\n * This method adds a new storage provider to the manager's registry and optionally\n * sets it as the default provider for subsequent operations. If no default provider\n * is currently set, the first registered provider automatically becomes the default.\n * @param name - Unique identifier for the provider\n * @param provider - The storage provider instance implementing the `StorageProvider` interface\n * @param isDefault - Whether this provider should be set as the default (defaults to `false`)\n * @example\n * ```typescript\n * const pinata = new PinataStorage({ jwt: 'your-jwt-token' });\n * storage.register('pinata', pinata, true); // Set as default\n *\n * const ipfs = new IPFSStorage({ apiEndpoint: 'https://...' });\n * storage.register('ipfs', ipfs); // Not default\n * ```\n */\n register(name: string, provider: StorageProvider, isDefault = false): void {\n this.providers.set(name, provider);\n\n if (isDefault || this.defaultProvider === null) {\n this.defaultProvider = name;\n }\n }\n\n /**\n * Get a registered storage provider\n *\n * @param name - Provider identifier, uses default if not specified\n * @returns Storage provider instance\n */\n getProvider(name?: string): StorageProvider {\n const providerName = name ?? this.defaultProvider;\n\n if (!providerName) {\n throw new StorageError(\n \"No storage provider specified and no default provider set\",\n \"NO_PROVIDER\",\n \"manager\",\n );\n }\n\n const provider = this.providers.get(providerName);\n if (!provider) {\n throw new StorageError(\n `Storage provider '${providerName}' not found`,\n \"PROVIDER_NOT_FOUND\",\n \"manager\",\n );\n }\n\n return provider;\n }\n\n /**\n * List all registered providers\n *\n * @returns Array of provider names\n */\n listProviders(): string[] {\n return Array.from(this.providers.keys());\n }\n\n /**\n * Get the default provider name\n *\n * @returns Default provider name or null\n */\n getDefaultProvider(): string | null {\n return this.defaultProvider;\n }\n\n /**\n * Set the default provider\n *\n * @param name - Provider identifier\n */\n setDefaultProvider(name: string): void {\n if (!this.providers.has(name)) {\n throw new StorageError(\n `Cannot set default provider '${name}': provider not registered`,\n \"PROVIDER_NOT_FOUND\",\n \"manager\",\n );\n }\n this.defaultProvider = name;\n }\n\n /**\n * Uploads a file using the specified or default storage provider.\n *\n * @remarks\n * This method uploads a file to the specified provider or falls back to the default\n * provider if none is specified. The upload result includes the storage URL, file size,\n * content type, and provider-specific metadata that can be used for subsequent operations.\n * @param file - The file blob to upload\n * @param filename - Optional custom filename (defaults to auto-generated name)\n * @param providerName - Optional provider identifier (uses default if not specified)\n * @returns A Promise that resolves to the storage upload result with URL and metadata\n * @throws {StorageError} When no provider is available or upload fails\n * @example\n * ```typescript\n * // Upload to default provider\n * const result = await storage.upload(fileBlob, 'data.json');\n * console.log(`Uploaded to: ${result.url}`);\n *\n * // Upload to specific provider\n * const result2 = await storage.upload(fileBlob, 'data.json', 'pinata');\n * ```\n */\n async upload(\n file: Blob,\n filename?: string,\n providerName?: string,\n ): Promise<StorageUploadResult> {\n const provider = this.getProvider(providerName);\n return provider.upload(file, filename);\n }\n\n /**\n * Download a file using the specified or default provider\n *\n * @param url - The storage URL\n * @param providerName - Optional provider to use\n * @returns Promise with file blob\n */\n async download(url: string, providerName?: string): Promise<Blob> {\n const provider = this.getProvider(providerName);\n return provider.download(url);\n }\n\n /**\n * List files using the specified or default provider\n *\n * @param options - Optional filtering and pagination\n * @param providerName - Optional provider to use\n * @returns Promise with file list\n */\n async list(\n options?: StorageListOptions,\n providerName?: string,\n ): Promise<StorageFile[]> {\n const provider = this.getProvider(providerName);\n return provider.list(options);\n }\n\n /**\n * Delete a file using the specified or default provider\n *\n * @param url - The storage URL\n * @param providerName - Optional provider to use\n * @returns Promise with success status\n */\n async delete(url: string, providerName?: string): Promise<boolean> {\n const provider = this.getProvider(providerName);\n return provider.delete(url);\n }\n\n /**\n * Get list of registered storage provider names\n *\n * @returns Array of provider names\n */\n getStorageProviders(): string[] {\n return Array.from(this.providers.keys());\n }\n\n /**\n * Get the default storage provider name\n *\n * @returns Default provider name or undefined\n */\n getDefaultStorageProvider(): string | undefined {\n return this.defaultProvider ?? undefined;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,qBAA6B;AAwCtB,MAAM,eAAe;AAAA,EAClB,YAA0C,oBAAI,IAAI;AAAA,EAClD,kBAAiC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBzC,SAAS,MAAc,UAA2B,YAAY,OAAa;AACzE,SAAK,UAAU,IAAI,MAAM,QAAQ;AAEjC,QAAI,aAAa,KAAK,oBAAoB,MAAM;AAC9C,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,YAAY,MAAgC;AAC1C,UAAM,eAAe,QAAQ,KAAK;AAElC,QAAI,CAAC,cAAc;AACjB,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,KAAK,UAAU,IAAI,YAAY;AAChD,QAAI,CAAC,UAAU;AACb,YAAM,IAAI;AAAA,QACR,qBAAqB,YAAY;AAAA,QACjC;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,gBAA0B;AACxB,WAAO,MAAM,KAAK,KAAK,UAAU,KAAK,CAAC;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,qBAAoC;AAClC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,mBAAmB,MAAoB;AACrC,QAAI,CAAC,KAAK,UAAU,IAAI,IAAI,GAAG;AAC7B,YAAM,IAAI;AAAA,QACR,gCAAgC,IAAI;AAAA,QACpC;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,SAAK,kBAAkB;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBA,MAAM,OACJ,MACA,UACA,cAC8B;AAC9B,UAAM,WAAW,KAAK,YAAY,YAAY;AAC9C,WAAO,SAAS,OAAO,MAAM,QAAQ;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,SAAS,KAAa,cAAsC;AAChE,UAAM,WAAW,KAAK,YAAY,YAAY;AAC9C,WAAO,SAAS,SAAS,GAAG;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,KACJ,SACA,cACwB;AACxB,UAAM,WAAW,KAAK,YAAY,YAAY;AAC9C,WAAO,SAAS,KAAK,OAAO;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,OAAO,KAAa,cAAyC;AACjE,UAAM,WAAW,KAAK,YAAY,YAAY;AAC9C,WAAO,SAAS,OAAO,GAAG;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,sBAAgC;AAC9B,WAAO,MAAM,KAAK,KAAK,UAAU,KAAK,CAAC;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,4BAAgD;AAC9C,WAAO,KAAK,mBAAmB;AAAA,EACjC;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/storage/manager.ts"],"sourcesContent":["/**\n * Manages multiple storage providers with a unified interface.\n *\n * @remarks\n * This module provides centralized management of storage providers, enabling\n * applications to work with multiple storage backends through a single API.\n * It handles provider registration, default selection, and operation routing.\n *\n * @category Storage\n * @module storage/manager\n */\n\nimport type {\n StorageProvider,\n StorageUploadResult,\n StorageFile,\n StorageListOptions,\n} from \"../types/storage\";\nimport { StorageError } from \"../types/storage\";\n\n/**\n * Manages multiple storage providers with a unified interface for file operations.\n *\n * @remarks\n * The StorageManager provides a consistent API for uploading, downloading, and managing\n * files across different storage backends including IPFS, Pinata, Google Drive, and\n * server-managed storage. It handles provider registration, default provider selection,\n * and automatic fallback scenarios for robust file operations.\n *\n * Used internally by DataController for encrypted file storage, but can also be used\n * directly for custom storage workflows. Each provider implements the `StorageProvider`\n * interface to ensure consistent behavior across different storage backends.\n *\n * The manager supports provider-specific configurations and features while maintaining\n * a uniform API surface for applications.\n * @example\n * ```typescript\n * import { StorageManager, IPFSStorage, PinataStorage } from '@opendatalabs/vana-sdk';\n *\n * const storage = new StorageManager();\n *\n * // Register multiple providers\n * storage.register('ipfs', new IPFSStorage({\n * apiEndpoint: 'https://api.pinata.cloud/pinning/pinFileToIPFS'\n * }), true);\n * storage.register('pinata', new PinataStorage({\n * jwt: 'your-pinata-jwt-token'\n * }));\n *\n * // Upload to default provider\n * const result = await storage.upload(fileBlob, 'myfile.json');\n *\n * // Upload to specific provider\n * const result2 = await storage.upload(fileBlob, 'myfile.json', 'pinata');\n * ```\n * @category Storage\n * @see {@link https://docs.vana.com/developer/vana-sdk-documentation/core-modules/storage-providers | Storage Providers Guide} for configuration details\n */\nexport class StorageManager {\n private providers: Map<string, StorageProvider> = new Map();\n private defaultProvider: string | null = null;\n\n /**\n * Registers a storage provider with the manager.\n *\n * @remarks\n * This method adds a new storage provider to the manager's registry and optionally\n * sets it as the default provider for subsequent operations. If no default provider\n * is currently set, the first registered provider automatically becomes the default.\n * @param name - Unique identifier for the provider\n * @param provider - The storage provider instance implementing the `StorageProvider` interface\n * @param isDefault - Whether this provider should be set as the default (defaults to `false`)\n * @example\n * ```typescript\n * const pinata = new PinataStorage({ jwt: 'your-jwt-token' });\n * storage.register('pinata', pinata, true); // Set as default\n *\n * const ipfs = new IPFSStorage({ apiEndpoint: 'https://...' });\n * storage.register('ipfs', ipfs); // Not default\n * ```\n */\n register(name: string, provider: StorageProvider, isDefault = false): void {\n this.providers.set(name, provider);\n\n if (isDefault || this.defaultProvider === null) {\n this.defaultProvider = name;\n }\n }\n\n /**\n * Retrieves a registered storage provider.\n *\n * @param name - Provider identifier.\n * If not specified, returns the default provider.\n * @returns The requested storage provider instance\n *\n * @throws {StorageError} With code 'NO_PROVIDER' if no provider available\n * @throws {StorageError} With code 'PROVIDER_NOT_FOUND' if named provider doesn't exist\n *\n * @example\n * ```typescript\n * const provider = storage.getProvider('pinata');\n * const config = provider.getConfig();\n * ```\n */\n getProvider(name?: string): StorageProvider {\n const providerName = name ?? this.defaultProvider;\n\n if (!providerName) {\n throw new StorageError(\n \"No storage provider specified and no default provider set\",\n \"NO_PROVIDER\",\n \"manager\",\n );\n }\n\n const provider = this.providers.get(providerName);\n if (!provider) {\n throw new StorageError(\n `Storage provider '${providerName}' not found`,\n \"PROVIDER_NOT_FOUND\",\n \"manager\",\n );\n }\n\n return provider;\n }\n\n /**\n * Lists all registered provider names.\n *\n * @returns Array of registered provider identifiers\n *\n * @example\n * ```typescript\n * const providers = storage.listProviders();\n * console.log('Available providers:', providers);\n * // Output: ['ipfs', 'pinata', 'google-drive']\n * ```\n */\n listProviders(): string[] {\n return Array.from(this.providers.keys());\n }\n\n /**\n * Gets the current default provider name.\n *\n * @returns Default provider identifier or null if none set\n *\n * @example\n * ```typescript\n * const defaultName = storage.getDefaultProvider();\n * if (defaultName) {\n * console.log(`Using ${defaultName} by default`);\n * }\n * ```\n */\n getDefaultProvider(): string | null {\n return this.defaultProvider;\n }\n\n /**\n * Sets the default storage provider.\n *\n * @param name - Provider identifier to set as default.\n * Must be a registered provider name.\n *\n * @throws {StorageError} With code 'PROVIDER_NOT_FOUND' if provider not registered\n *\n * @example\n * ```typescript\n * storage.setDefaultProvider('pinata');\n * // Now all operations without provider name will use Pinata\n * ```\n */\n setDefaultProvider(name: string): void {\n if (!this.providers.has(name)) {\n throw new StorageError(\n `Cannot set default provider '${name}': provider not registered`,\n \"PROVIDER_NOT_FOUND\",\n \"manager\",\n );\n }\n this.defaultProvider = name;\n }\n\n /**\n * Uploads a file using the specified or default storage provider.\n *\n * @remarks\n * This method uploads a file to the specified provider or falls back to the default\n * provider if none is specified. The upload result includes the storage URL, file size,\n * content type, and provider-specific metadata that can be used for subsequent operations.\n * @param file - The file blob to upload\n * @param filename - Optional custom filename (defaults to auto-generated name)\n * @param providerName - Optional provider identifier (uses default if not specified)\n * @returns A Promise that resolves to the storage upload result with URL and metadata\n * @throws {StorageError} When no provider is available or upload fails\n * @example\n * ```typescript\n * // Upload to default provider\n * const result = await storage.upload(fileBlob, 'data.json');\n * console.log(`Uploaded to: ${result.url}`);\n *\n * // Upload to specific provider\n * const result2 = await storage.upload(fileBlob, 'data.json', 'pinata');\n * ```\n */\n async upload(\n file: Blob,\n filename?: string,\n providerName?: string,\n ): Promise<StorageUploadResult> {\n const provider = this.getProvider(providerName);\n return provider.upload(file, filename);\n }\n\n /**\n * Downloads a file from storage.\n *\n * @param url - The storage URL to download from.\n * Format depends on the storage provider.\n * @param providerName - Optional provider identifier.\n * Uses default provider if not specified.\n * @returns The downloaded file as a Blob\n *\n * @throws {StorageError} If download fails or provider unavailable\n *\n * @example\n * ```typescript\n * const blob = await storage.download('ipfs://QmXxx...');\n * const text = await blob.text();\n * ```\n */\n async download(url: string, providerName?: string): Promise<Blob> {\n const provider = this.getProvider(providerName);\n return provider.download(url);\n }\n\n /**\n * Lists files in storage.\n *\n * @param options - Optional filtering and pagination.\n * @param options.namePattern - Pattern to filter files.\n * @param options.limit - Maximum files to return.\n * @param providerName - Optional provider identifier.\n * Uses default provider if not specified.\n * @returns Array of file metadata\n *\n * @throws {StorageError} If listing fails or not supported by provider\n *\n * @example\n * ```typescript\n * const files = await storage.list(\n * { namePattern: '*.json', limit: 10 },\n * 'google-drive'\n * );\n * ```\n */\n async list(\n options?: StorageListOptions,\n providerName?: string,\n ): Promise<StorageFile[]> {\n const provider = this.getProvider(providerName);\n return provider.list(options);\n }\n\n /**\n * Deletes a file from storage.\n *\n * @param url - The storage URL to delete.\n * Must be a valid URL for the provider.\n * @param providerName - Optional provider identifier.\n * Uses default provider if not specified.\n * @returns True if deletion succeeded, false otherwise\n *\n * @throws {StorageError} If deletion fails or not supported by provider\n *\n * @example\n * ```typescript\n * const success = await storage.delete('ipfs://QmXxx...');\n * if (success) {\n * console.log('File deleted successfully');\n * }\n * ```\n */\n async delete(url: string, providerName?: string): Promise<boolean> {\n const provider = this.getProvider(providerName);\n return provider.delete(url);\n }\n\n /**\n * Gets all registered storage provider names.\n *\n * @returns Array of provider identifiers\n *\n * @deprecated Use `listProviders()` instead\n *\n * @example\n * ```typescript\n * const providers = storage.getStorageProviders();\n * ```\n */\n getStorageProviders(): string[] {\n return Array.from(this.providers.keys());\n }\n\n /**\n * Gets the default storage provider name.\n *\n * @returns Default provider identifier or undefined if none set\n *\n * @deprecated Use `getDefaultProvider()` instead\n *\n * @example\n * ```typescript\n * const defaultProvider = storage.getDefaultStorageProvider();\n * ```\n */\n getDefaultStorageProvider(): string | undefined {\n return this.defaultProvider ?? undefined;\n }\n}\n"],"mappings":"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","names":[]}
|