@directus/storage-driver-supabase 2.0.1 → 2.1.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/dist/index.d.ts +13 -3
- package/dist/index.js +84 -1
- package/package.json +8 -7
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TusDriver, ReadOptions, ChunkedUploadContext } from '@directus/storage';
|
|
2
2
|
import { Readable } from 'node:stream';
|
|
3
3
|
|
|
4
4
|
type DriverSupabaseConfig = {
|
|
@@ -8,18 +8,23 @@ type DriverSupabaseConfig = {
|
|
|
8
8
|
/** Allows a custom Supabase endpoint for self-hosting */
|
|
9
9
|
endpoint?: string;
|
|
10
10
|
root?: string;
|
|
11
|
+
tus?: {
|
|
12
|
+
chunkSize?: number;
|
|
13
|
+
};
|
|
11
14
|
};
|
|
12
|
-
declare class DriverSupabase implements
|
|
15
|
+
declare class DriverSupabase implements TusDriver {
|
|
13
16
|
private config;
|
|
14
17
|
private client;
|
|
15
18
|
private bucket;
|
|
19
|
+
private readonly preferredChunkSize;
|
|
16
20
|
constructor(config: DriverSupabaseConfig);
|
|
17
21
|
private get endpoint();
|
|
18
22
|
private getClient;
|
|
19
23
|
private getBucket;
|
|
20
24
|
private fullPath;
|
|
21
25
|
private getAuthenticatedUrl;
|
|
22
|
-
|
|
26
|
+
private getResumableUrl;
|
|
27
|
+
read(filepath: string, options?: ReadOptions): Promise<Readable>;
|
|
23
28
|
stat(filepath: string): Promise<{
|
|
24
29
|
size: any;
|
|
25
30
|
modified: Date;
|
|
@@ -31,6 +36,11 @@ declare class DriverSupabase implements Driver {
|
|
|
31
36
|
delete(filepath: string): Promise<void>;
|
|
32
37
|
list(prefix?: string): AsyncIterable<string>;
|
|
33
38
|
listGenerator(prefix: string): AsyncIterable<string>;
|
|
39
|
+
get tusExtensions(): string[];
|
|
40
|
+
createChunkedUpload(_filepath: string, context: ChunkedUploadContext): Promise<ChunkedUploadContext>;
|
|
41
|
+
writeChunk(filepath: string, content: Readable, offset: number, context: ChunkedUploadContext): Promise<number>;
|
|
42
|
+
finishChunkedUpload(_filepath: string, _context: ChunkedUploadContext): Promise<void>;
|
|
43
|
+
deleteChunkedUpload(filepath: string, _context: ChunkedUploadContext): Promise<void>;
|
|
34
44
|
}
|
|
35
45
|
|
|
36
46
|
export { DriverSupabase, type DriverSupabaseConfig, DriverSupabase as default };
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { normalizePath } from "@directus/utils";
|
|
3
3
|
import { StorageClient } from "@supabase/storage-js";
|
|
4
|
+
import * as tus from "tus-js-client";
|
|
4
5
|
import { join } from "node:path";
|
|
5
6
|
import { Readable } from "node:stream";
|
|
6
7
|
import { fetch } from "undici";
|
|
8
|
+
var DEFAULT_CHUNK_SIZE = 1e7;
|
|
7
9
|
var DriverSupabase = class {
|
|
8
10
|
config;
|
|
9
11
|
client;
|
|
10
12
|
bucket;
|
|
13
|
+
// TUS specific members
|
|
14
|
+
preferredChunkSize;
|
|
11
15
|
constructor(config) {
|
|
12
16
|
this.config = {
|
|
13
17
|
...config,
|
|
14
18
|
root: normalizePath(config.root ?? "", { removeLeading: true })
|
|
15
19
|
};
|
|
20
|
+
this.preferredChunkSize = this.config.tus?.chunkSize ?? DEFAULT_CHUNK_SIZE;
|
|
16
21
|
this.client = this.getClient();
|
|
17
22
|
this.bucket = this.getBucket();
|
|
18
23
|
}
|
|
@@ -45,7 +50,11 @@ var DriverSupabase = class {
|
|
|
45
50
|
getAuthenticatedUrl(filepath) {
|
|
46
51
|
return `${this.endpoint}/${join("object/authenticated", this.config.bucket, this.fullPath(filepath))}`;
|
|
47
52
|
}
|
|
48
|
-
|
|
53
|
+
getResumableUrl() {
|
|
54
|
+
return `${this.endpoint}/upload/resumable`;
|
|
55
|
+
}
|
|
56
|
+
async read(filepath, options) {
|
|
57
|
+
const { range } = options || {};
|
|
49
58
|
const requestInit = { method: "GET" };
|
|
50
59
|
requestInit.headers = {
|
|
51
60
|
Authorization: `Bearer ${this.config.serviceRole}`
|
|
@@ -129,11 +138,85 @@ var DriverSupabase = class {
|
|
|
129
138
|
}
|
|
130
139
|
} while (itemCount === limit);
|
|
131
140
|
}
|
|
141
|
+
get tusExtensions() {
|
|
142
|
+
return ["creation", "termination", "expiration"];
|
|
143
|
+
}
|
|
144
|
+
async createChunkedUpload(_filepath, context) {
|
|
145
|
+
return context;
|
|
146
|
+
}
|
|
147
|
+
async writeChunk(filepath, content, offset, context) {
|
|
148
|
+
let bytesUploaded = offset || 0;
|
|
149
|
+
const metadata = {
|
|
150
|
+
bucketName: this.config.bucket,
|
|
151
|
+
objectName: this.fullPath(filepath),
|
|
152
|
+
contentType: context.metadata["type"] ?? "image/png",
|
|
153
|
+
cacheControl: "3600"
|
|
154
|
+
};
|
|
155
|
+
await new Promise((resolve, reject) => {
|
|
156
|
+
const upload = new tus.Upload(content, {
|
|
157
|
+
endpoint: this.getResumableUrl(),
|
|
158
|
+
// @ts-expect-error
|
|
159
|
+
fileReader: new FileReader(),
|
|
160
|
+
headers: {
|
|
161
|
+
Authorization: `Bearer ${this.config.serviceRole}`,
|
|
162
|
+
"x-upsert": "true"
|
|
163
|
+
},
|
|
164
|
+
metadata,
|
|
165
|
+
chunkSize: this.preferredChunkSize,
|
|
166
|
+
uploadSize: context.size,
|
|
167
|
+
retryDelays: null,
|
|
168
|
+
onError(error) {
|
|
169
|
+
reject(error);
|
|
170
|
+
},
|
|
171
|
+
onChunkComplete: function(chunkSize) {
|
|
172
|
+
bytesUploaded += chunkSize;
|
|
173
|
+
resolve(null);
|
|
174
|
+
},
|
|
175
|
+
onSuccess() {
|
|
176
|
+
resolve(null);
|
|
177
|
+
},
|
|
178
|
+
onUploadUrlAvailable() {
|
|
179
|
+
if (!context.metadata["upload-url"]) {
|
|
180
|
+
context.metadata["upload-url"] = upload.url;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
if (context.metadata["upload-url"]) {
|
|
185
|
+
upload.resumeFromPreviousUpload({
|
|
186
|
+
size: context.size,
|
|
187
|
+
creationTime: context.metadata["creation_date"],
|
|
188
|
+
metadata,
|
|
189
|
+
uploadUrl: context.metadata["upload-url"]
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
upload.start();
|
|
193
|
+
});
|
|
194
|
+
return bytesUploaded;
|
|
195
|
+
}
|
|
196
|
+
async finishChunkedUpload(_filepath, _context) {
|
|
197
|
+
}
|
|
198
|
+
async deleteChunkedUpload(filepath, _context) {
|
|
199
|
+
await this.delete(filepath);
|
|
200
|
+
}
|
|
132
201
|
};
|
|
133
202
|
var src_default = DriverSupabase;
|
|
134
203
|
function dirname(path) {
|
|
135
204
|
return path.split("/").slice(0, -1).join("/");
|
|
136
205
|
}
|
|
206
|
+
var StreamSource2 = class extends tus.StreamSource {
|
|
207
|
+
_streamEnded = false;
|
|
208
|
+
// @ts-expect-error
|
|
209
|
+
async slice(start, end) {
|
|
210
|
+
if (this._streamEnded) return null;
|
|
211
|
+
this._streamEnded = true;
|
|
212
|
+
return super.slice(0, end - start);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
var FileReader = class {
|
|
216
|
+
async openFile(input, _) {
|
|
217
|
+
return new StreamSource2(input);
|
|
218
|
+
}
|
|
219
|
+
};
|
|
137
220
|
export {
|
|
138
221
|
DriverSupabase,
|
|
139
222
|
src_default as default
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@directus/storage-driver-supabase",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Supabase file storage abstraction for `@directus/storage`",
|
|
5
5
|
"homepage": "https://directus.io",
|
|
6
6
|
"repository": {
|
|
@@ -21,16 +21,17 @@
|
|
|
21
21
|
"dist"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@supabase/storage-js": "2.
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"@directus/storage": "11.0.
|
|
24
|
+
"@supabase/storage-js": "2.7.1",
|
|
25
|
+
"tus-js-client": "4.2.3",
|
|
26
|
+
"undici": "6.20.0",
|
|
27
|
+
"@directus/storage": "11.0.1",
|
|
28
|
+
"@directus/utils": "12.0.3"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
31
|
"@ngneat/falso": "7.2.0",
|
|
31
32
|
"@vitest/coverage-v8": "2.1.2",
|
|
32
|
-
"tsup": "8.
|
|
33
|
-
"typescript": "5.6.
|
|
33
|
+
"tsup": "8.3.0",
|
|
34
|
+
"typescript": "5.6.3",
|
|
34
35
|
"vitest": "2.1.2",
|
|
35
36
|
"@directus/tsconfig": "2.0.0"
|
|
36
37
|
},
|