@directus/storage-driver-supabase 1.0.10 → 1.0.12

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,3 @@
1
- import * as undici from 'undici';
2
1
  import { Driver, Range } from '@directus/storage';
3
2
  import { Readable } from 'node:stream';
4
3
 
@@ -6,6 +5,7 @@ type DriverSupabaseConfig = {
6
5
  bucket: string;
7
6
  serviceRole: string;
8
7
  projectId?: string;
8
+ /** Allows a custom Supabase endpoint for self-hosting */
9
9
  endpoint?: string;
10
10
  root?: string;
11
11
  };
@@ -17,12 +17,11 @@ declare class DriverSupabase implements Driver {
17
17
  private get endpoint();
18
18
  private getClient;
19
19
  private getBucket;
20
- private getFullPath;
20
+ private fullPath;
21
21
  private getAuthenticatedUrl;
22
22
  read(filepath: string, range?: Range): Promise<Readable>;
23
- head(filepath: string): Promise<undici.Headers>;
24
23
  stat(filepath: string): Promise<{
25
- size: number;
24
+ size: any;
26
25
  modified: Date;
27
26
  }>;
28
27
  exists(filepath: string): Promise<boolean>;
@@ -30,7 +29,8 @@ declare class DriverSupabase implements Driver {
30
29
  copy(src: string, dest: string): Promise<void>;
31
30
  write(filepath: string, content: Readable, type?: string): Promise<void>;
32
31
  delete(filepath: string): Promise<void>;
33
- list(prefix?: string): AsyncGenerator<string, void, unknown>;
32
+ list(prefix?: string): AsyncIterable<string>;
33
+ listGenerator(prefix: string): AsyncIterable<string>;
34
34
  }
35
35
 
36
36
  export { DriverSupabase, type DriverSupabaseConfig, DriverSupabase as default };
package/dist/index.js CHANGED
@@ -9,7 +9,10 @@ var DriverSupabase = class {
9
9
  client;
10
10
  bucket;
11
11
  constructor(config) {
12
- this.config = config;
12
+ this.config = {
13
+ ...config,
14
+ root: normalizePath(config.root ?? "", { removeLeading: true })
15
+ };
13
16
  this.client = this.getClient();
14
17
  this.bucket = this.getBucket();
15
18
  }
@@ -34,11 +37,14 @@ var DriverSupabase = class {
34
37
  }
35
38
  return this.client.from(this.config.bucket);
36
39
  }
37
- getFullPath(filepath) {
38
- return this.config.root ? normalizePath(join(this.config.root, filepath)) : filepath;
40
+ fullPath(filepath) {
41
+ const path = join(this.config.root, filepath);
42
+ if (path === ".")
43
+ return "";
44
+ return normalizePath(path);
39
45
  }
40
46
  getAuthenticatedUrl(filepath) {
41
- return `${this.endpoint}/${join("object/authenticated", this.config.bucket, this.getFullPath(filepath))}`;
47
+ return `${this.endpoint}/${join("object/authenticated", this.config.bucket, this.fullPath(filepath))}`;
42
48
  }
43
49
  async read(filepath, range) {
44
50
  const requestInit = { method: "GET" };
@@ -54,23 +60,17 @@ var DriverSupabase = class {
54
60
  }
55
61
  return Readable.fromWeb(response.body);
56
62
  }
57
- async head(filepath) {
58
- const response = await fetch(this.getAuthenticatedUrl(filepath), {
59
- method: "HEAD",
60
- headers: {
61
- Authorization: `Bearer ${this.config.serviceRole}`
62
- }
63
+ async stat(filepath) {
64
+ const { data, error } = await this.bucket.list(this.config.root, {
65
+ search: filepath,
66
+ limit: 1
63
67
  });
64
- if (response.status >= 400) {
68
+ if (error || data.length === 0) {
65
69
  throw new Error("File not found");
66
70
  }
67
- return response.headers;
68
- }
69
- async stat(filepath) {
70
- const headers = await this.head(filepath);
71
71
  return {
72
- size: parseInt(headers.get("content-length") || ""),
73
- modified: new Date(headers.get("last-modified") || "")
72
+ size: data[0]?.metadata["contentLength"] ?? 0,
73
+ modified: new Date(data[0]?.metadata["lastModified"] || null)
74
74
  };
75
75
  }
76
76
  async exists(filepath) {
@@ -82,13 +82,13 @@ var DriverSupabase = class {
82
82
  }
83
83
  }
84
84
  async move(src, dest) {
85
- await this.bucket.move(this.getFullPath(src), this.getFullPath(dest));
85
+ await this.bucket.move(this.fullPath(src), this.fullPath(dest));
86
86
  }
87
87
  async copy(src, dest) {
88
- await this.bucket.copy(this.getFullPath(src), this.getFullPath(dest));
88
+ await this.bucket.copy(this.fullPath(src), this.fullPath(dest));
89
89
  }
90
90
  async write(filepath, content, type) {
91
- await this.bucket.upload(this.getFullPath(filepath), content, {
91
+ await this.bucket.upload(this.fullPath(filepath), content, {
92
92
  contentType: type ?? "",
93
93
  cacheControl: "3600",
94
94
  upsert: true,
@@ -96,26 +96,45 @@ var DriverSupabase = class {
96
96
  });
97
97
  }
98
98
  async delete(filepath) {
99
- await this.bucket.remove([this.getFullPath(filepath)]);
99
+ await this.bucket.remove([this.fullPath(filepath)]);
100
+ }
101
+ list(prefix = "") {
102
+ const fullPrefix = this.fullPath(prefix);
103
+ return this.listGenerator(fullPrefix);
100
104
  }
101
- async *list(prefix = "") {
105
+ async *listGenerator(prefix) {
102
106
  const limit = 1e3;
103
107
  let offset = 0;
104
108
  let itemCount = 0;
109
+ const isDirectory = prefix.endsWith("/");
110
+ const prefixDirectory = isDirectory ? prefix : dirname(prefix);
111
+ const search = isDirectory ? "" : prefix.split("/").pop() ?? "";
105
112
  do {
106
- const { data, error } = await this.bucket.list(this.config.root, { limit, offset, search: prefix });
113
+ const { data, error } = await this.bucket.list(prefixDirectory, {
114
+ limit,
115
+ offset,
116
+ search
117
+ });
107
118
  if (!data || error) {
108
119
  break;
109
120
  }
110
121
  itemCount = data.length;
111
122
  offset += itemCount;
112
123
  for (const item of data) {
113
- yield item.name;
124
+ const filePath = normalizePath(join(prefixDirectory, item.name));
125
+ if (item.id !== null) {
126
+ yield filePath.substring(this.config.root ? this.config.root.length + 1 : 0);
127
+ } else {
128
+ yield* this.listGenerator(`${filePath}/`);
129
+ }
114
130
  }
115
131
  } while (itemCount === limit);
116
132
  }
117
133
  };
118
134
  var src_default = DriverSupabase;
135
+ function dirname(path) {
136
+ return path.split("/").slice(0, -1).join("/");
137
+ }
119
138
  export {
120
139
  DriverSupabase,
121
140
  src_default as default
package/license CHANGED
@@ -1,7 +1,7 @@
1
1
  Licensor: Monospace, Inc.
2
2
 
3
3
  Licensed Work: Directus
4
- The Licensed Work is Copyright © 2023 Monospace, Inc.
4
+ The Licensed Work is Copyright © 2024 Monospace, Inc.
5
5
 
6
6
  Additional Use Grant: You may use the Licensed Work in production as long as
7
7
  your Total Finances do not exceed US $5,000,000 for the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@directus/storage-driver-supabase",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "description": "Supabase file storage abstraction for `@directus/storage`",
5
5
  "homepage": "https://directus.io",
6
6
  "repository": {
@@ -22,16 +22,16 @@
22
22
  ],
23
23
  "dependencies": {
24
24
  "@supabase/storage-js": "2.5.5",
25
- "undici": "6.7.0",
26
- "@directus/utils": "11.0.6",
27
- "@directus/storage": "10.0.11"
25
+ "undici": "6.15.0",
26
+ "@directus/storage": "10.0.12",
27
+ "@directus/utils": "11.0.8"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@ngneat/falso": "7.2.0",
31
- "@vitest/coverage-v8": "1.3.1",
31
+ "@vitest/coverage-v8": "1.5.3",
32
32
  "tsup": "8.0.2",
33
- "typescript": "5.3.3",
34
- "vitest": "1.3.1",
33
+ "typescript": "5.4.5",
34
+ "vitest": "1.5.3",
35
35
  "@directus/tsconfig": "1.0.1"
36
36
  },
37
37
  "scripts": {