@meistrari/vault-sdk 0.0.12 → 1.0.0
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/README.md +102 -41
- package/dist/index.cjs +443 -0
- package/dist/index.d.cts +264 -0
- package/dist/index.d.mts +219 -42
- package/dist/index.d.ts +219 -42
- package/dist/index.mjs +330 -79
- package/package.json +16 -13
package/README.md
CHANGED
|
@@ -11,28 +11,91 @@ ni @meistrari/vault-sdk-sdk
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
13
|
The main component of the SDK is the `VaultFile` class.
|
|
14
|
-
It
|
|
14
|
+
It provides methods to upload, download, and reference files in the vault.
|
|
15
|
+
|
|
16
|
+
### Creating Files
|
|
17
|
+
|
|
18
|
+
To create a new file in the vault, use `VaultFile.fromContent`:
|
|
15
19
|
|
|
16
20
|
```ts
|
|
17
|
-
|
|
21
|
+
const vaultFile = await VaultFile.fromContent({
|
|
22
|
+
name: 'document.txt', // Original filename
|
|
23
|
+
content: new File(['content'], 'document.txt'), // File content
|
|
24
|
+
config: {
|
|
25
|
+
vaultUrl,
|
|
26
|
+
authStrategy: new DataTokenAuthStrategy(dataToken)
|
|
27
|
+
},
|
|
28
|
+
upload: true // Optional: upload immediately
|
|
29
|
+
})
|
|
30
|
+
```
|
|
18
31
|
|
|
19
|
-
|
|
20
|
-
|
|
32
|
+
### Retrieving Files
|
|
33
|
+
|
|
34
|
+
To retrieve a file using a vault reference, use `VaultFile.fromVaultReference`:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
const vaultFile = await VaultFile.fromVaultReference({
|
|
38
|
+
reference: 'vault://1234567890', // The vault reference
|
|
39
|
+
config: {
|
|
40
|
+
vaultUrl,
|
|
41
|
+
authStrategy: new DataTokenAuthStrategy(dataToken)
|
|
42
|
+
},
|
|
43
|
+
download: false // Optional: download immediately if true
|
|
44
|
+
})
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Basic Usage Flow
|
|
48
|
+
|
|
49
|
+
The typical workflow with the SDK involves:
|
|
50
|
+
|
|
51
|
+
1. Creating a file in the vault using `VaultFile.fromContent`
|
|
52
|
+
2. Getting a reference to that file with `getVaultReference()`
|
|
53
|
+
3. Later, retrieving the file using `VaultFile.fromVaultReference`
|
|
21
54
|
|
|
22
|
-
|
|
23
|
-
const localFile = Bun.file('path/to/local/file.txt')
|
|
55
|
+
Here's an example showing the complete flow:
|
|
24
56
|
|
|
25
|
-
|
|
57
|
+
```ts
|
|
58
|
+
import { VaultFile, DataTokenAuthStrategy } from '@meistrari/vault-sdk'
|
|
26
59
|
|
|
27
|
-
const
|
|
60
|
+
const dataToken = '[some-data-token]'
|
|
61
|
+
const vaultUrl = Bun.env.VAULT_URL ?? 'https://vault.tela.com'
|
|
28
62
|
|
|
29
|
-
|
|
63
|
+
// Step 1: Create and upload a new file
|
|
64
|
+
const fileContent = Bun.file('path/to/local/file.txt')
|
|
65
|
+
const vaultFile = await VaultFile.fromContent({
|
|
66
|
+
name: 'file.txt',
|
|
67
|
+
content: fileContent,
|
|
68
|
+
config: {
|
|
69
|
+
vaultUrl,
|
|
70
|
+
authStrategy: new DataTokenAuthStrategy(dataToken)
|
|
71
|
+
},
|
|
72
|
+
upload: true // Upload immediately
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
// Step 2: Get a reference to store in your database or application
|
|
76
|
+
const vaultReference = vaultFile.getVaultReference() // e.g. 'vault://1234567890'
|
|
77
|
+
console.log(`Store this reference: ${vaultReference}`)
|
|
78
|
+
|
|
79
|
+
// ... Later, in another part of your application ...
|
|
80
|
+
|
|
81
|
+
// Step 3: Retrieve the file using the reference
|
|
82
|
+
const retrievedFile = await VaultFile.fromVaultReference({
|
|
83
|
+
reference: vaultReference,
|
|
84
|
+
config: {
|
|
85
|
+
vaultUrl,
|
|
86
|
+
authStrategy: new DataTokenAuthStrategy(dataToken)
|
|
87
|
+
},
|
|
88
|
+
download: false // Set to true to download immediately
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// Step 4: Download the content when needed
|
|
92
|
+
const fileContent = await retrievedFile.download()
|
|
93
|
+
console.log(fileContent)
|
|
30
94
|
```
|
|
31
95
|
|
|
32
|
-
###
|
|
96
|
+
### Using the useVault helper
|
|
33
97
|
|
|
34
|
-
|
|
35
|
-
It takes the vault url and an auth strategy, and returns a function that creates a `VaultFile` instance using those parameters.
|
|
98
|
+
The SDK provides a convenient `useVault` helper function that simplifies creating vault files:
|
|
36
99
|
|
|
37
100
|
```ts
|
|
38
101
|
import { useVault, DataTokenAuthStrategy } from '@meistrari/vault-sdk'
|
|
@@ -40,17 +103,28 @@ import { useVault, DataTokenAuthStrategy } from '@meistrari/vault-sdk'
|
|
|
40
103
|
const dataToken = '[some-data-token]'
|
|
41
104
|
const vaultUrl = Bun.env.VAULT_URL ?? 'https://vault.tela.com'
|
|
42
105
|
|
|
43
|
-
|
|
44
|
-
const
|
|
106
|
+
// Create a vault instance
|
|
107
|
+
const vault = useVault({
|
|
108
|
+
vaultUrl,
|
|
109
|
+
authStrategy: new DataTokenAuthStrategy(dataToken)
|
|
110
|
+
})
|
|
45
111
|
|
|
46
|
-
|
|
112
|
+
// Create a file from content
|
|
113
|
+
const fileFromContent = vault.createFromContent(
|
|
114
|
+
'document.txt',
|
|
115
|
+
new File(['content'], 'document.txt')
|
|
116
|
+
)
|
|
47
117
|
|
|
48
|
-
|
|
118
|
+
// Create a file from a vault reference
|
|
119
|
+
const fileFromReference = vault.createFromReference('vault://1234567890')
|
|
120
|
+
```
|
|
49
121
|
|
|
50
|
-
|
|
122
|
+
The `useVault` helper provides two methods:
|
|
51
123
|
|
|
52
|
-
|
|
53
|
-
|
|
124
|
+
- `createFromContent(name, content)`: Creates a new `VaultFile` instance from a name and content (Blob or File)
|
|
125
|
+
- `createFromReference(reference)`: Creates a new `VaultFile` instance from a vault reference string
|
|
126
|
+
|
|
127
|
+
This helper makes it more convenient to work with multiple vault files using the same configuration.
|
|
54
128
|
|
|
55
129
|
### Auth strategies
|
|
56
130
|
|
|
@@ -72,9 +146,16 @@ const authStrategy = new DataTokenAuthStrategy(dataToken)
|
|
|
72
146
|
#### APIKeyAuthStrategy
|
|
73
147
|
|
|
74
148
|
It takes an API key, and passes it as the `Authorization` header in requests to the vault.
|
|
75
|
-
Use this when performing external calls
|
|
149
|
+
Use this when performing external calls **through the API Gateway**, such as from the frontend.
|
|
76
150
|
Since the clerk token is also passed as the `Authorization` header, this strategy can also be used with the value of the `__session` cookie instead of an API key.
|
|
77
|
-
|
|
151
|
+
|
|
152
|
+
> [!WARNING]
|
|
153
|
+
> The vault service itself does not support API Keys, it only understands data tokens.
|
|
154
|
+
> To use this strategy, the request **must** go through the API Gateway.
|
|
155
|
+
> For that, the gateway URL should be used instead of the vault service URL.
|
|
156
|
+
>
|
|
157
|
+
> Use this: `https://staging.api.tela.com/_services/vault`.<br>
|
|
158
|
+
> Instead of this: `https://vault.tela.com`
|
|
78
159
|
|
|
79
160
|
```ts
|
|
80
161
|
import { APIKeyAuthStrategy } from '@meistrari/vault-sdk'
|
|
@@ -83,23 +164,3 @@ const apiKey = '[some-api-key]'
|
|
|
83
164
|
|
|
84
165
|
const authStrategy = new APIKeyAuthStrategy(apiKey)
|
|
85
166
|
```
|
|
86
|
-
|
|
87
|
-
### Retrieving files with a vault ref
|
|
88
|
-
|
|
89
|
-
Both the `VaultFile` class and the `createVaultFile` function accept a vault ref as the name of the file.
|
|
90
|
-
The vault ref is a string with the form `vault://file-name.ext`.
|
|
91
|
-
The workspace ID is obtained from the authentication data, and doesn't need to be explicitly passed.
|
|
92
|
-
|
|
93
|
-
```ts
|
|
94
|
-
import { useVault, DataTokenAuthStrategy } from '@meistrari/vault-sdk'
|
|
95
|
-
|
|
96
|
-
const dataToken = '[some-data-token]'
|
|
97
|
-
const vaultUrl = Bun.env.VAULT_URL ?? 'https://vault.tela.com'
|
|
98
|
-
|
|
99
|
-
const { createVaultFile } = useVault(vaultUrl, new DataTokenAuthStrategy(dataToken))
|
|
100
|
-
const vaultFile = createVaultFile('vault://file-name.ext')
|
|
101
|
-
|
|
102
|
-
const downloadedFile = await vaultFile.download()
|
|
103
|
-
|
|
104
|
-
console.log(downloadedFile)
|
|
105
|
-
```
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const schemas = require('@meistrari/vault-shared/schemas');
|
|
4
|
+
|
|
5
|
+
class FetchError extends Error {
|
|
6
|
+
constructor(message, url, method, response) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.message = message;
|
|
9
|
+
this.url = url;
|
|
10
|
+
this.method = method;
|
|
11
|
+
this.response = response;
|
|
12
|
+
this.name = "FetchError";
|
|
13
|
+
}
|
|
14
|
+
static async from(url, method, response) {
|
|
15
|
+
const text = await response.clone().json().then((json) => JSON.stringify(json, null, 2)).catch(() => response.clone().text());
|
|
16
|
+
const error = new FetchError(`Failed to ${method} ${url}: ${response.status} ${response.statusText}:
|
|
17
|
+
${text}`, url, method, response);
|
|
18
|
+
return error;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function blobToBase64(blob) {
|
|
23
|
+
const fileContent = new Uint8Array(await blob.arrayBuffer());
|
|
24
|
+
let content = "";
|
|
25
|
+
for (const part of fileContent)
|
|
26
|
+
content += String.fromCharCode(part);
|
|
27
|
+
return btoa(content);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function getFileHash(blob) {
|
|
31
|
+
const arrayBuffer = await blob.arrayBuffer();
|
|
32
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", arrayBuffer);
|
|
33
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
34
|
+
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
35
|
+
return hashHex;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
var __defProp$1 = Object.defineProperty;
|
|
39
|
+
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
40
|
+
var __publicField$1 = (obj, key, value) => {
|
|
41
|
+
__defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
42
|
+
return value;
|
|
43
|
+
};
|
|
44
|
+
const compatibilityDate = "2025-05-19";
|
|
45
|
+
function removeVaultPrefix(url) {
|
|
46
|
+
return url.replace("vault://", "");
|
|
47
|
+
}
|
|
48
|
+
async function wrappedFetch(...params) {
|
|
49
|
+
const request = new Request(...params);
|
|
50
|
+
const response = await fetch(request);
|
|
51
|
+
if (!response.ok) {
|
|
52
|
+
throw await FetchError.from(request.url, request.method, response);
|
|
53
|
+
}
|
|
54
|
+
return response;
|
|
55
|
+
}
|
|
56
|
+
class VaultFile {
|
|
57
|
+
/**
|
|
58
|
+
* Constructs a new VaultFile instance. Direct usage of the constructor is not recommended,
|
|
59
|
+
* instead use the static methods {@link VaultFile.fromVaultReference} when dealing with an existing file in the vault,
|
|
60
|
+
* or {@link VaultFile.fromContent} when preparing a new file for upload.
|
|
61
|
+
*
|
|
62
|
+
* @param params - The parameters for the VaultFile constructor
|
|
63
|
+
* @param params.config - The configuration for the VaultFile
|
|
64
|
+
* @param params.content - The content of the file
|
|
65
|
+
* @param params.id - The ID of the file
|
|
66
|
+
* @param params.name - The name of the file
|
|
67
|
+
* @param params.metadata - The metadata of the file
|
|
68
|
+
*/
|
|
69
|
+
constructor({ config, content, id, name, metadata }) {
|
|
70
|
+
__publicField$1(this, "id");
|
|
71
|
+
__publicField$1(this, "name");
|
|
72
|
+
__publicField$1(this, "metadata");
|
|
73
|
+
__publicField$1(this, "config");
|
|
74
|
+
__publicField$1(this, "content");
|
|
75
|
+
__publicField$1(this, "lastDownloadUrl");
|
|
76
|
+
__publicField$1(this, "lastUploadUrl");
|
|
77
|
+
this.config = config;
|
|
78
|
+
this.content = content;
|
|
79
|
+
this.id = id;
|
|
80
|
+
this.name = name;
|
|
81
|
+
this.metadata = metadata;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Gets the headers for the request based on the auth strategy.
|
|
85
|
+
*
|
|
86
|
+
* @returns The headers for the request
|
|
87
|
+
*/
|
|
88
|
+
get headers() {
|
|
89
|
+
return this.config.authStrategy.getHeaders();
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Performs a request to the vault service and handles the response or errors.
|
|
93
|
+
*
|
|
94
|
+
* @param params - The parameters for the fetch
|
|
95
|
+
* @param params.method - The method to use for the fetch
|
|
96
|
+
* @param params.path - The path to fetch
|
|
97
|
+
* @param params.body - The body of the request
|
|
98
|
+
* @returns The response from the vault
|
|
99
|
+
* @throws {FetchError} If the fetch fails
|
|
100
|
+
*/
|
|
101
|
+
async _fetch(params) {
|
|
102
|
+
const { method, path, body } = params;
|
|
103
|
+
const url = new URL(this.config.vaultUrl + path).toString();
|
|
104
|
+
const headers = new Headers(this.headers);
|
|
105
|
+
headers.set("x-compatibility-date", compatibilityDate);
|
|
106
|
+
const response = await wrappedFetch(url, {
|
|
107
|
+
method,
|
|
108
|
+
body,
|
|
109
|
+
headers
|
|
110
|
+
});
|
|
111
|
+
const content = await response.json();
|
|
112
|
+
return content;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Creates a new file in the vault.
|
|
116
|
+
*
|
|
117
|
+
* @returns The metadata of the file
|
|
118
|
+
* @throws {Error} If the file ID is not set
|
|
119
|
+
* @throws {FetchError} If the metadata fetch fails
|
|
120
|
+
*/
|
|
121
|
+
async _createFile() {
|
|
122
|
+
const response = await this._fetch({
|
|
123
|
+
method: "POST",
|
|
124
|
+
path: `/v2/files`,
|
|
125
|
+
body: JSON.stringify({
|
|
126
|
+
fileName: this.name,
|
|
127
|
+
sha256sum: this.id ?? this.metadata?.id ?? (this.content ? await getFileHash(this.content) : void 0)
|
|
128
|
+
})
|
|
129
|
+
}).then((data) => schemas.GetUploadUrlResponseV2.safeParse(data));
|
|
130
|
+
if (!response.success) {
|
|
131
|
+
throw new Error(`Invalid response from vault service. ${JSON.stringify(response.error)}`);
|
|
132
|
+
}
|
|
133
|
+
this.id = response.data.id;
|
|
134
|
+
this.metadata = response.data.metadata;
|
|
135
|
+
this.name = response.data.metadata?.originalFileName;
|
|
136
|
+
return response.data;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Creates a new VaultFile instance from a vault reference.
|
|
140
|
+
*
|
|
141
|
+
* @param params - The parameters for creating a VaultFile from a vault reference
|
|
142
|
+
* @param params.reference - The reference to the file in the vault
|
|
143
|
+
* @param params.config - The configuration for the VaultFile
|
|
144
|
+
* @param params.download - Whether to download the file content (default: false)
|
|
145
|
+
* @returns A new VaultFile instance
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```ts
|
|
149
|
+
* // Lazily download the file content
|
|
150
|
+
* const vaultFile = await VaultFile.fromVaultReference({
|
|
151
|
+
* reference: 'vault://1234567890',
|
|
152
|
+
* config: {
|
|
153
|
+
* vaultUrl,
|
|
154
|
+
* authStrategy,
|
|
155
|
+
* }
|
|
156
|
+
* })
|
|
157
|
+
* const content = await vaultFile.download()
|
|
158
|
+
* ```
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* // Download the file content while creating the instance
|
|
163
|
+
* const vaultFile = await VaultFile.fromVaultReference({
|
|
164
|
+
* reference: 'vault://1234567890',
|
|
165
|
+
* config: {
|
|
166
|
+
* vaultUrl,
|
|
167
|
+
* authStrategy,
|
|
168
|
+
* },
|
|
169
|
+
* download: true
|
|
170
|
+
* })
|
|
171
|
+
* const content = vaultFile.content
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
static async fromVaultReference(params) {
|
|
175
|
+
const { reference, config, download = false } = params;
|
|
176
|
+
const { vaultUrl, authStrategy } = config;
|
|
177
|
+
const id = removeVaultPrefix(reference);
|
|
178
|
+
const response = await wrappedFetch(`${vaultUrl}/v2/files/${id}`, {
|
|
179
|
+
method: "GET",
|
|
180
|
+
headers: authStrategy.getHeaders()
|
|
181
|
+
}).then((response2) => response2.json()).then((data) => schemas.GetDownloadUrlResponse.safeParse(data));
|
|
182
|
+
if (!response.success) {
|
|
183
|
+
throw new Error("Invalid response from vault service");
|
|
184
|
+
}
|
|
185
|
+
const fileParams = {
|
|
186
|
+
id,
|
|
187
|
+
metadata: response.data.metadata,
|
|
188
|
+
config: {
|
|
189
|
+
vaultUrl,
|
|
190
|
+
authStrategy
|
|
191
|
+
},
|
|
192
|
+
name: response.data.metadata?.originalFileName
|
|
193
|
+
};
|
|
194
|
+
if (download) {
|
|
195
|
+
await wrappedFetch(response.data.url, { method: "GET" }).then((response2) => response2.blob()).then((blob) => fileParams.content = blob);
|
|
196
|
+
}
|
|
197
|
+
return new VaultFile(fileParams);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Creates a new VaultFile instance from given content.
|
|
201
|
+
*
|
|
202
|
+
* @param params - The parameters for creating a VaultFile from content
|
|
203
|
+
* @param params.name - The name of the file
|
|
204
|
+
* @param params.content - The content of the file
|
|
205
|
+
* @param params.config - The configuration for the VaultFile
|
|
206
|
+
* @param params.upload - Whether to upload the file (default: false)
|
|
207
|
+
* @returns A new VaultFile instance
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```ts
|
|
211
|
+
* // Lazily upload the file content
|
|
212
|
+
* const file = new File(['content'], 'document.txt')
|
|
213
|
+
* const vaultFile = await VaultFile.fromContent({
|
|
214
|
+
* name: 'document.txt',
|
|
215
|
+
* content: file,
|
|
216
|
+
* config: {
|
|
217
|
+
* vaultUrl,
|
|
218
|
+
* authStrategy,
|
|
219
|
+
* }
|
|
220
|
+
* })
|
|
221
|
+
* await vaultFile.upload()
|
|
222
|
+
* ```
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```ts
|
|
226
|
+
* // Upload the file content while creating the instance
|
|
227
|
+
* const file = new File(['content'], 'document.txt')
|
|
228
|
+
* const vaultFile = await VaultFile.fromContent({
|
|
229
|
+
* name: 'document.txt',
|
|
230
|
+
* content: file,
|
|
231
|
+
* config: {
|
|
232
|
+
* vaultUrl,
|
|
233
|
+
* authStrategy,
|
|
234
|
+
* },
|
|
235
|
+
* upload: true
|
|
236
|
+
* })
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
static async fromContent(params) {
|
|
240
|
+
const { name, content, config, upload = false } = params;
|
|
241
|
+
const { vaultUrl, authStrategy } = config;
|
|
242
|
+
const sha256sum = await getFileHash(content);
|
|
243
|
+
const file = new VaultFile({
|
|
244
|
+
content,
|
|
245
|
+
config: {
|
|
246
|
+
vaultUrl,
|
|
247
|
+
authStrategy
|
|
248
|
+
},
|
|
249
|
+
id: sha256sum,
|
|
250
|
+
name
|
|
251
|
+
});
|
|
252
|
+
const createdFile = await file._createFile();
|
|
253
|
+
if (upload) {
|
|
254
|
+
await file.upload(file.content, createdFile.uploadUrl);
|
|
255
|
+
}
|
|
256
|
+
return file;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Populates the metadata of the file instance.
|
|
260
|
+
*
|
|
261
|
+
* @returns The file instance
|
|
262
|
+
* @throws {Error} If the file ID is not set
|
|
263
|
+
* @throws {FetchError} If the metadata fetch fails
|
|
264
|
+
*/
|
|
265
|
+
async populateMetadata() {
|
|
266
|
+
try {
|
|
267
|
+
this.metadata = await this.getFileMetadata();
|
|
268
|
+
this.name = this.metadata.originalFileName;
|
|
269
|
+
this.id = this.metadata.id;
|
|
270
|
+
return this;
|
|
271
|
+
} catch (error) {
|
|
272
|
+
console.error("Error fetching file metadata", error);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Gets the vault reference for this file.
|
|
277
|
+
*
|
|
278
|
+
* @returns The vault reference in the format `vault://{fileId}`
|
|
279
|
+
* @throws {Error} If the file ID is not set
|
|
280
|
+
*/
|
|
281
|
+
getVaultReference() {
|
|
282
|
+
if (!this.id) {
|
|
283
|
+
throw new Error("File ID is not set");
|
|
284
|
+
}
|
|
285
|
+
return `vault://${this.id}`;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Fetches the metadata of the file.
|
|
289
|
+
*
|
|
290
|
+
* @returns The metadata of the file
|
|
291
|
+
* @throws {Error} If the file ID is not set
|
|
292
|
+
* @throws {FetchError} If the metadata fetch fails
|
|
293
|
+
*/
|
|
294
|
+
async getFileMetadata() {
|
|
295
|
+
if (!this.id) {
|
|
296
|
+
throw new Error("File ID is not set");
|
|
297
|
+
}
|
|
298
|
+
const response = await this._fetch({
|
|
299
|
+
method: "GET",
|
|
300
|
+
path: `/v2/files/${this.id}/metadata`
|
|
301
|
+
});
|
|
302
|
+
return response;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Fetches a upload URL for the file.
|
|
306
|
+
*
|
|
307
|
+
* @returns The upload URL for the file
|
|
308
|
+
* @throws {Error} If the vault service returns an invalid response
|
|
309
|
+
* @throws {FetchError} If the upload URL fetch fails
|
|
310
|
+
*/
|
|
311
|
+
async getUploadUrl() {
|
|
312
|
+
if (this.lastUploadUrl && this.lastUploadUrl.expiresAt > /* @__PURE__ */ new Date()) {
|
|
313
|
+
return this.lastUploadUrl.url;
|
|
314
|
+
}
|
|
315
|
+
if (!this.id) {
|
|
316
|
+
const createdFile = await this._createFile();
|
|
317
|
+
this.id = createdFile.id;
|
|
318
|
+
this.metadata = createdFile.metadata;
|
|
319
|
+
this.name = createdFile.metadata?.originalFileName;
|
|
320
|
+
this.lastUploadUrl = { url: new URL(createdFile.uploadUrl), expiresAt: new Date(createdFile.expiresAt) };
|
|
321
|
+
return this.lastUploadUrl.url;
|
|
322
|
+
}
|
|
323
|
+
const response = await this._fetch({
|
|
324
|
+
method: "PUT",
|
|
325
|
+
path: `/v2/files/${this.id}`
|
|
326
|
+
}).then(schemas.GetUploadUrlResponseV2.safeParse);
|
|
327
|
+
if (!response.success) {
|
|
328
|
+
throw new Error(`Invalid response from vault service. ${JSON.stringify(response.error)}`);
|
|
329
|
+
}
|
|
330
|
+
this.lastUploadUrl = { url: new URL(response.data.uploadUrl), expiresAt: new Date(response.data.expiresAt) };
|
|
331
|
+
return this.lastUploadUrl.url;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Fetches a download URL for the file.
|
|
335
|
+
*
|
|
336
|
+
* @returns The download URL for the file
|
|
337
|
+
* @throws {Error} If the vault service returns an invalid response
|
|
338
|
+
* @throws {Error} If not file ID, name or content is set
|
|
339
|
+
* @throws {FetchError} If the download URL fetch fails
|
|
340
|
+
*/
|
|
341
|
+
async getDownloadUrl() {
|
|
342
|
+
if (this.lastDownloadUrl && this.lastDownloadUrl.expiresAt > /* @__PURE__ */ new Date()) {
|
|
343
|
+
return this.lastDownloadUrl.url;
|
|
344
|
+
}
|
|
345
|
+
if (!this.id && !this.name && !this.content) {
|
|
346
|
+
throw new Error("File was not created yet");
|
|
347
|
+
}
|
|
348
|
+
const id = this.id ?? this.metadata?.id ?? (this.content ? await getFileHash(this.content) : this.name);
|
|
349
|
+
const response = await this._fetch({
|
|
350
|
+
method: "GET",
|
|
351
|
+
path: `/v2/files/${id}`
|
|
352
|
+
});
|
|
353
|
+
this.lastDownloadUrl = { url: new URL(response.url), expiresAt: new Date(response.expiresAt) };
|
|
354
|
+
return this.lastDownloadUrl.url;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Uploads a file to the vault.
|
|
358
|
+
*
|
|
359
|
+
* @example
|
|
360
|
+
* ```ts
|
|
361
|
+
* const file = new File(['content'], 'document.txt')
|
|
362
|
+
* const vaultFile = await VaultFile.fromBlob('document.txt', file, { vaultUrl, authStrategy })
|
|
363
|
+
* await vaultFile.upload(file)
|
|
364
|
+
* ```
|
|
365
|
+
*
|
|
366
|
+
* @param file - The file to upload to the vault. If not provided, the file content will be taken from the `content` property.
|
|
367
|
+
* @throws {FetchError} If the upload fails
|
|
368
|
+
* @throws {Error} If the file content is not set and no file is provided
|
|
369
|
+
* @returns Promise that resolves when upload is complete
|
|
370
|
+
*/
|
|
371
|
+
async upload(file, url) {
|
|
372
|
+
if (!file && !this.content) {
|
|
373
|
+
throw new Error("Missing file content. Use fromBlob() to create a file with content, or provide a file to upload.");
|
|
374
|
+
}
|
|
375
|
+
const uploadUrl = url ?? await this.getUploadUrl();
|
|
376
|
+
await wrappedFetch(uploadUrl, {
|
|
377
|
+
method: "PUT",
|
|
378
|
+
body: file ?? this.content
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
async download(responseType = "blob") {
|
|
382
|
+
const downloadUrl = await this.getDownloadUrl();
|
|
383
|
+
const response = await wrappedFetch(downloadUrl, {
|
|
384
|
+
method: "GET"
|
|
385
|
+
});
|
|
386
|
+
const blob = await response.blob();
|
|
387
|
+
if (responseType === "blob")
|
|
388
|
+
return blob;
|
|
389
|
+
return await blobToBase64(blob);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
var __defProp = Object.defineProperty;
|
|
394
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
395
|
+
var __publicField = (obj, key, value) => {
|
|
396
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
397
|
+
return value;
|
|
398
|
+
};
|
|
399
|
+
class DataTokenAuthStrategy {
|
|
400
|
+
constructor(dataToken) {
|
|
401
|
+
__publicField(this, "dataToken");
|
|
402
|
+
this.dataToken = dataToken;
|
|
403
|
+
}
|
|
404
|
+
getHeaders() {
|
|
405
|
+
return new Headers({
|
|
406
|
+
"x-data-token": this.dataToken
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
class APIKeyAuthStrategy {
|
|
411
|
+
constructor(apiKey) {
|
|
412
|
+
__publicField(this, "apiKey");
|
|
413
|
+
this.apiKey = apiKey;
|
|
414
|
+
}
|
|
415
|
+
getHeaders() {
|
|
416
|
+
return new Headers({
|
|
417
|
+
Authorization: this.apiKey
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function vaultClient(config) {
|
|
423
|
+
function createFromContent(name, content) {
|
|
424
|
+
return VaultFile.fromContent({
|
|
425
|
+
name,
|
|
426
|
+
content,
|
|
427
|
+
config
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
function createFromReference(reference) {
|
|
431
|
+
return VaultFile.fromVaultReference({
|
|
432
|
+
reference,
|
|
433
|
+
config
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
return { createFromContent, createFromReference };
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
exports.APIKeyAuthStrategy = APIKeyAuthStrategy;
|
|
440
|
+
exports.DataTokenAuthStrategy = DataTokenAuthStrategy;
|
|
441
|
+
exports.FetchError = FetchError;
|
|
442
|
+
exports.VaultFile = VaultFile;
|
|
443
|
+
exports.vaultClient = vaultClient;
|