@directus/storage-driver-supabase 2.0.2 → 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 CHANGED
@@ -1,4 +1,4 @@
1
- import { Driver, ReadOptions } from '@directus/storage';
1
+ import { TusDriver, ReadOptions, ChunkedUploadContext } from '@directus/storage';
2
2
  import { Readable } from 'node:stream';
3
3
 
4
4
  type DriverSupabaseConfig = {
@@ -8,17 +8,22 @@ 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 Driver {
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;
26
+ private getResumableUrl;
22
27
  read(filepath: string, options?: ReadOptions): Promise<Readable>;
23
28
  stat(filepath: string): Promise<{
24
29
  size: any;
@@ -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,6 +50,9 @@ var DriverSupabase = class {
45
50
  getAuthenticatedUrl(filepath) {
46
51
  return `${this.endpoint}/${join("object/authenticated", this.config.bucket, this.fullPath(filepath))}`;
47
52
  }
53
+ getResumableUrl() {
54
+ return `${this.endpoint}/upload/resumable`;
55
+ }
48
56
  async read(filepath, options) {
49
57
  const { range } = options || {};
50
58
  const requestInit = { method: "GET" };
@@ -130,11 +138,85 @@ var DriverSupabase = class {
130
138
  }
131
139
  } while (itemCount === limit);
132
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
+ }
133
201
  };
134
202
  var src_default = DriverSupabase;
135
203
  function dirname(path) {
136
204
  return path.split("/").slice(0, -1).join("/");
137
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
+ };
138
220
  export {
139
221
  DriverSupabase,
140
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.2",
3
+ "version": "2.1.0",
4
4
  "description": "Supabase file storage abstraction for `@directus/storage`",
5
5
  "homepage": "https://directus.io",
6
6
  "repository": {
@@ -22,9 +22,10 @@
22
22
  ],
23
23
  "dependencies": {
24
24
  "@supabase/storage-js": "2.7.1",
25
+ "tus-js-client": "4.2.3",
25
26
  "undici": "6.20.0",
26
- "@directus/utils": "12.0.2",
27
- "@directus/storage": "11.0.1"
27
+ "@directus/storage": "11.0.1",
28
+ "@directus/utils": "12.0.3"
28
29
  },
29
30
  "devDependencies": {
30
31
  "@ngneat/falso": "7.2.0",