img-upload-kit 1.1.1 → 2.0.1
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/CHANGELOG.md +89 -74
- package/MIGRATION.md +192 -0
- package/README.md +243 -259
- package/dist/bin.js +1186 -0
- package/dist/bin.js.map +1 -0
- package/dist/blur.cjs +40 -412
- package/dist/blur.cjs.map +1 -1
- package/dist/blur.d.cts +5 -5
- package/dist/blur.d.ts +5 -5
- package/dist/blur.js +39 -393
- package/dist/blur.js.map +1 -1
- package/dist/blurDataUtils-DveBRtqE.d.cts +43 -0
- package/dist/blurDataUtils-yx9Q6HlU.d.ts +43 -0
- package/dist/blurService-D5TpMNji.d.ts +83 -0
- package/dist/blurService-DacwylJx.d.cts +83 -0
- package/dist/chunk-25GYKQ6J.js +158 -0
- package/dist/chunk-25GYKQ6J.js.map +1 -0
- package/dist/chunk-34L4TEE3.cjs +226 -0
- package/dist/chunk-34L4TEE3.cjs.map +1 -0
- package/dist/chunk-DFCSN3X7.cjs +158 -0
- package/dist/chunk-DFCSN3X7.cjs.map +1 -0
- package/dist/chunk-EWVIL57J.js +294 -0
- package/dist/chunk-EWVIL57J.js.map +1 -0
- package/dist/chunk-FP5YIWG5.cjs +135 -0
- package/dist/chunk-FP5YIWG5.cjs.map +1 -0
- package/dist/chunk-HILELQGS.js +226 -0
- package/dist/chunk-HILELQGS.js.map +1 -0
- package/dist/chunk-IDLLCVGB.js +2370 -0
- package/dist/chunk-IDLLCVGB.js.map +1 -0
- package/dist/chunk-J6A44Y3W.js +514 -0
- package/dist/chunk-J6A44Y3W.js.map +1 -0
- package/dist/chunk-LOSVFO56.cjs +145 -0
- package/dist/chunk-LOSVFO56.cjs.map +1 -0
- package/dist/chunk-MBQJ6NVJ.cjs +294 -0
- package/dist/chunk-MBQJ6NVJ.cjs.map +1 -0
- package/dist/chunk-MHGQEBPW.js +145 -0
- package/dist/chunk-MHGQEBPW.js.map +1 -0
- package/dist/chunk-QHU7FUO4.cjs +37 -0
- package/dist/chunk-QHU7FUO4.cjs.map +1 -0
- package/dist/chunk-S2IFAWJK.js +37 -0
- package/dist/chunk-S2IFAWJK.js.map +1 -0
- package/dist/chunk-TA5Y64JM.cjs +2370 -0
- package/dist/chunk-TA5Y64JM.cjs.map +1 -0
- package/dist/chunk-VYVZVUNI.js +135 -0
- package/dist/chunk-VYVZVUNI.js.map +1 -0
- package/dist/chunk-XKQMCY5Z.cjs +514 -0
- package/dist/chunk-XKQMCY5Z.cjs.map +1 -0
- package/dist/cli.cjs +273 -76
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +269 -73
- package/dist/cli.js.map +1 -1
- package/dist/headless.cjs +8 -1880
- package/dist/headless.cjs.map +1 -1
- package/dist/headless.d.cts +3 -1
- package/dist/headless.d.ts +3 -1
- package/dist/headless.js +25 -1886
- package/dist/headless.js.map +1 -1
- package/dist/index-BIdnqtW8.d.ts +234 -0
- package/dist/index-Brk_uHs6.d.cts +234 -0
- package/dist/{index-FVXXKf2v.d.ts → index-CPMlaQsC.d.ts} +76 -153
- package/dist/{index-FVXXKf2v.d.cts → index-DHINe-rA.d.cts} +76 -153
- package/dist/index.cjs +64 -532
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.js +63 -504
- package/dist/index.js.map +1 -1
- package/dist/{memoryAdapter-CBu7_3Pp.d.ts → memoryAdapter-Ck0ccZbF.d.cts} +3 -2
- package/dist/{memoryAdapter-BJjyLR-1.d.cts → memoryAdapter-DxEKaG0b.d.ts} +3 -2
- package/dist/placeholders-C4ftwaII.d.ts +5 -0
- package/dist/placeholders-x_3rt65M.d.cts +5 -0
- package/dist/react.cjs +1952 -3257
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +188 -50
- package/dist/react.d.ts +188 -50
- package/dist/react.js +1763 -3013
- package/dist/react.js.map +1 -1
- package/dist/server-handler.cjs +14 -0
- package/dist/server-handler.cjs.map +1 -0
- package/dist/server-handler.d.cts +3 -0
- package/dist/server-handler.d.ts +3 -0
- package/dist/server-handler.js +14 -0
- package/dist/server-handler.js.map +1 -0
- package/dist/server.cjs +1096 -532
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +77 -154
- package/dist/server.d.ts +77 -154
- package/dist/server.js +1050 -448
- package/dist/server.js.map +1 -1
- package/dist/storage.cjs +239 -232
- package/dist/storage.cjs.map +1 -1
- package/dist/storage.d.cts +21 -5
- package/dist/storage.d.ts +21 -5
- package/dist/storage.js +227 -207
- package/dist/storage.js.map +1 -1
- package/dist/styles/animated-image.css +112 -0
- package/dist/styles/dropzone-and-tiles.css +247 -0
- package/dist/styles/modals-and-dock.css +248 -0
- package/dist/styles/primitives.css +197 -0
- package/dist/styles/quality-and-inputs.css +184 -0
- package/dist/styles/tokens.css +123 -0
- package/dist/styles.css +235 -220
- package/dist/types-BrP-o9nd.d.cts +85 -0
- package/dist/types-sZ3KoFQY.d.ts +85 -0
- package/dist/types-yShGNQnW.d.cts +61 -0
- package/dist/types-yShGNQnW.d.ts +61 -0
- package/dist/{types-CiLY0DwB.d.cts → upload-C5Trd6Dh.d.cts} +27 -53
- package/dist/{types-CiLY0DwB.d.ts → upload-C5Trd6Dh.d.ts} +27 -53
- package/package.json +92 -32
- package/dist/blurService-DqsnfPTd.d.cts +0 -172
- package/dist/blurService-DqsnfPTd.d.ts +0 -172
package/dist/storage.cjs
CHANGED
|
@@ -1,60 +1,123 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
2
|
|
|
3
|
-
var fs = require('fs/promises');
|
|
4
|
-
var path = require('path');
|
|
5
3
|
|
|
6
|
-
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
var
|
|
5
|
+
|
|
6
|
+
var _chunkLOSVFO56cjs = require('./chunk-LOSVFO56.cjs');
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
var _chunkQHU7FUO4cjs = require('./chunk-QHU7FUO4.cjs');
|
|
10
11
|
|
|
11
12
|
// src/storage/localFsAdapter.ts
|
|
13
|
+
var _promises = require('fs/promises'); var _promises2 = _interopRequireDefault(_promises);
|
|
14
|
+
var _path = require('path'); var _path2 = _interopRequireDefault(_path);
|
|
12
15
|
function sanitizeRelativePath(inputPath) {
|
|
16
|
+
if (!inputPath || typeof inputPath !== "string") {
|
|
17
|
+
throw new Error("Invalid storage path");
|
|
18
|
+
}
|
|
19
|
+
if (inputPath.includes(":") || inputPath.includes("\0")) {
|
|
20
|
+
throw new Error("Path traversal blocked in LocalFsStorageAdapter");
|
|
21
|
+
}
|
|
13
22
|
const normalized = inputPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
14
|
-
const
|
|
15
|
-
if (
|
|
23
|
+
const rawSegments = normalized.split("/").filter(Boolean);
|
|
24
|
+
if (rawSegments.length === 0) {
|
|
16
25
|
throw new Error("Invalid storage path");
|
|
17
26
|
}
|
|
18
|
-
|
|
27
|
+
for (const segment of rawSegments) {
|
|
28
|
+
if (segment === "." || segment === "..") {
|
|
29
|
+
throw new Error("Invalid storage path: path traversal blocked");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return rawSegments.join("/");
|
|
33
|
+
}
|
|
34
|
+
function assertContainedInRoot(resolvedRoot, targetPath) {
|
|
35
|
+
const relative = _path2.default.relative(resolvedRoot, targetPath);
|
|
36
|
+
if (relative.startsWith("..") || _path2.default.isAbsolute(relative)) {
|
|
37
|
+
throw new Error("Path traversal blocked in LocalFsStorageAdapter");
|
|
38
|
+
}
|
|
19
39
|
}
|
|
20
40
|
function createLocalFsStorageAdapter(options) {
|
|
21
|
-
const resolvedRoot =
|
|
22
|
-
const baseUrl = (options.baseUrl
|
|
41
|
+
const resolvedRoot = _path2.default.resolve(options.rootDir);
|
|
42
|
+
const baseUrl = (_nullishCoalesce(options.baseUrl, () => ( "/uploads"))).replace(/\/+$/, "");
|
|
23
43
|
const includeBucket = options.includeBucketInPath !== false;
|
|
24
44
|
const resolveDiskTarget = (bucket, relativePath) => {
|
|
25
45
|
const cleanBucket = sanitizeRelativePath(bucket);
|
|
26
46
|
const cleanRel = sanitizeRelativePath(relativePath);
|
|
27
47
|
const subPath = includeBucket ? `${cleanBucket}/${cleanRel}` : cleanRel;
|
|
28
|
-
const diskPath =
|
|
29
|
-
|
|
30
|
-
throw new Error("Path traversal blocked in LocalFsStorageAdapter");
|
|
31
|
-
}
|
|
48
|
+
const diskPath = _path2.default.resolve(resolvedRoot, subPath);
|
|
49
|
+
assertContainedInRoot(resolvedRoot, diskPath);
|
|
32
50
|
return { diskPath, subPath, cleanRel };
|
|
33
51
|
};
|
|
34
52
|
const getPublicUrl = (bucket, relativePath) => {
|
|
35
53
|
const { subPath } = resolveDiskTarget(bucket, relativePath);
|
|
36
54
|
return `${baseUrl}/${subPath}`;
|
|
37
55
|
};
|
|
56
|
+
const parseUrl = (url) => {
|
|
57
|
+
if (!url || typeof url !== "string") return null;
|
|
58
|
+
try {
|
|
59
|
+
const cleanUrl = url.split(/[?#]/)[0];
|
|
60
|
+
const prefix = `${baseUrl}/`;
|
|
61
|
+
let subPath = null;
|
|
62
|
+
if (cleanUrl.startsWith(prefix)) {
|
|
63
|
+
subPath = cleanUrl.slice(prefix.length);
|
|
64
|
+
} else if (baseUrl.startsWith("/")) {
|
|
65
|
+
const parsed = new URL(cleanUrl, "http://localhost");
|
|
66
|
+
const isLocal = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
|
|
67
|
+
const isRelative = !cleanUrl.startsWith("http://") && !cleanUrl.startsWith("https://");
|
|
68
|
+
if ((isLocal || isRelative) && parsed.pathname.startsWith(prefix)) {
|
|
69
|
+
subPath = parsed.pathname.slice(prefix.length);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!subPath) return null;
|
|
73
|
+
const decoded = decodeURIComponent(subPath).replace(/\\/g, "/").replace(/^\/+/, "");
|
|
74
|
+
if (!decoded || decoded.includes(":") || decoded.includes("\0")) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
const segments = decoded.split("/").filter(Boolean);
|
|
78
|
+
if (segments.length === 0 || segments.includes("..") || segments.includes(".")) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const diskPath = _path2.default.resolve(resolvedRoot, ...segments);
|
|
82
|
+
const relative = _path2.default.relative(resolvedRoot, diskPath);
|
|
83
|
+
if (relative.startsWith("..") || _path2.default.isAbsolute(relative)) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
if (includeBucket) {
|
|
87
|
+
if (segments.length < 2) return null;
|
|
88
|
+
return {
|
|
89
|
+
bucket: segments[0],
|
|
90
|
+
path: segments.slice(1).join("/")
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return { path: segments.join("/") };
|
|
94
|
+
} catch (e2) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
38
98
|
return {
|
|
39
99
|
async upload(params) {
|
|
40
100
|
const { diskPath, cleanRel } = resolveDiskTarget(
|
|
41
101
|
params.bucket,
|
|
42
102
|
params.path
|
|
43
103
|
);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
104
|
+
await _promises2.default.mkdir(_path2.default.dirname(diskPath), { recursive: true });
|
|
105
|
+
try {
|
|
106
|
+
await _promises2.default.writeFile(
|
|
107
|
+
diskPath,
|
|
108
|
+
params.data,
|
|
109
|
+
params.upsert === false ? { flag: "wx" } : void 0
|
|
110
|
+
);
|
|
111
|
+
} catch (err) {
|
|
112
|
+
if (err.code === "EEXIST") {
|
|
113
|
+
const existsError = new Error(
|
|
48
114
|
`File already exists at ${params.bucket}/${cleanRel}`
|
|
49
115
|
);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
throw err;
|
|
53
|
-
}
|
|
116
|
+
existsError.code = "EEXIST";
|
|
117
|
+
throw existsError;
|
|
54
118
|
}
|
|
119
|
+
throw err;
|
|
55
120
|
}
|
|
56
|
-
await fs__default.default.mkdir(path__default.default.dirname(diskPath), { recursive: true });
|
|
57
|
-
await fs__default.default.writeFile(diskPath, params.data);
|
|
58
121
|
return {
|
|
59
122
|
bucket: params.bucket,
|
|
60
123
|
path: cleanRel,
|
|
@@ -64,66 +127,53 @@ function createLocalFsStorageAdapter(options) {
|
|
|
64
127
|
async delete(bucket, relativePath) {
|
|
65
128
|
const { diskPath } = resolveDiskTarget(bucket, relativePath);
|
|
66
129
|
try {
|
|
67
|
-
await
|
|
130
|
+
await _promises2.default.unlink(diskPath);
|
|
68
131
|
} catch (err) {
|
|
69
132
|
if (err.code !== "ENOENT") {
|
|
70
133
|
throw err;
|
|
71
134
|
}
|
|
72
135
|
}
|
|
73
136
|
},
|
|
137
|
+
async exists(bucket, relativePath) {
|
|
138
|
+
try {
|
|
139
|
+
const { diskPath } = resolveDiskTarget(bucket, relativePath);
|
|
140
|
+
const stat = await _promises2.default.stat(diskPath);
|
|
141
|
+
return stat.isFile();
|
|
142
|
+
} catch (e3) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
},
|
|
74
146
|
getPublicUrl,
|
|
147
|
+
parseUrl,
|
|
75
148
|
async deleteByUrl(url) {
|
|
76
|
-
|
|
149
|
+
const parsed = parseUrl(url);
|
|
150
|
+
if (!parsed) return false;
|
|
77
151
|
try {
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
subPath = cleanUrl.slice(prefix.length);
|
|
83
|
-
} else if (baseUrl.startsWith("/")) {
|
|
84
|
-
const parsed = new URL(cleanUrl, "http://localhost");
|
|
85
|
-
const isLocal = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
|
|
86
|
-
const isRelative = !cleanUrl.startsWith("http://") && !cleanUrl.startsWith("https://");
|
|
87
|
-
if ((isLocal || isRelative) && parsed.pathname.startsWith(prefix)) {
|
|
88
|
-
subPath = parsed.pathname.slice(prefix.length);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
if (!subPath) return false;
|
|
92
|
-
const decoded = decodeURIComponent(subPath).replace(/\\/g, "/").replace(/^\/+/, "");
|
|
93
|
-
if (!decoded) return false;
|
|
94
|
-
const segments = decoded.split("/").filter(Boolean);
|
|
95
|
-
if (segments.length === 0 || segments.includes("..") || segments.includes(".")) {
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
const diskPath = path__default.default.resolve(resolvedRoot, ...segments);
|
|
99
|
-
const relative = path__default.default.relative(resolvedRoot, diskPath);
|
|
100
|
-
if (relative.startsWith("..") || path__default.default.isAbsolute(relative)) {
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
await fs__default.default.unlink(diskPath);
|
|
152
|
+
const subSegments = includeBucket && parsed.bucket ? [parsed.bucket, ...parsed.path.split("/")] : parsed.path.split("/");
|
|
153
|
+
const diskPath = _path2.default.resolve(resolvedRoot, ...subSegments);
|
|
154
|
+
assertContainedInRoot(resolvedRoot, diskPath);
|
|
155
|
+
await _promises2.default.unlink(diskPath);
|
|
104
156
|
return true;
|
|
105
|
-
} catch {
|
|
157
|
+
} catch (e4) {
|
|
106
158
|
return false;
|
|
107
159
|
}
|
|
108
160
|
},
|
|
109
|
-
async list(bucket, folder) {
|
|
161
|
+
async list(bucket, folder, listOptions) {
|
|
110
162
|
const cleanBucket = sanitizeRelativePath(bucket);
|
|
111
163
|
const cleanFolder = folder ? sanitizeRelativePath(folder) : "";
|
|
112
|
-
const targetDir =
|
|
164
|
+
const targetDir = _path2.default.resolve(
|
|
113
165
|
resolvedRoot,
|
|
114
166
|
includeBucket ? cleanBucket : "",
|
|
115
167
|
cleanFolder
|
|
116
168
|
);
|
|
117
|
-
|
|
118
|
-
throw new Error("Path traversal blocked in LocalFsStorageAdapter");
|
|
119
|
-
}
|
|
169
|
+
assertContainedInRoot(resolvedRoot, targetDir);
|
|
120
170
|
try {
|
|
121
|
-
const dirEntries = await
|
|
171
|
+
const dirEntries = await _promises2.default.readdir(targetDir, { withFileTypes: true });
|
|
122
172
|
const results = [];
|
|
123
173
|
for (const entry of dirEntries) {
|
|
124
174
|
if (!entry.isFile()) continue;
|
|
125
|
-
const full =
|
|
126
|
-
const stat = await
|
|
175
|
+
const full = _path2.default.join(targetDir, entry.name);
|
|
176
|
+
const stat = await _promises2.default.stat(full);
|
|
127
177
|
results.push({
|
|
128
178
|
name: entry.name,
|
|
129
179
|
id: entry.name,
|
|
@@ -131,10 +181,14 @@ function createLocalFsStorageAdapter(options) {
|
|
|
131
181
|
size: stat.size
|
|
132
182
|
});
|
|
133
183
|
}
|
|
134
|
-
|
|
184
|
+
const offset = _optionalChain([listOptions, 'optionalAccess', _ => _.cursor]) ? Number(listOptions.cursor) || 0 : 0;
|
|
185
|
+
const limit = _nullishCoalesce(_optionalChain([listOptions, 'optionalAccess', _2 => _2.limit]), () => ( results.length));
|
|
186
|
+
const sliced = _optionalChain([listOptions, 'optionalAccess', _3 => _3.limit]) !== void 0 || offset > 0 ? results.slice(offset, offset + limit) : results;
|
|
187
|
+
const nextCursor = _optionalChain([listOptions, 'optionalAccess', _4 => _4.limit]) !== void 0 && offset + sliced.length < results.length ? String(offset + sliced.length) : void 0;
|
|
188
|
+
return _chunkLOSVFO56cjs.toPaginatedListResult.call(void 0, sliced, nextCursor);
|
|
135
189
|
} catch (err) {
|
|
136
190
|
if (err.code === "ENOENT") {
|
|
137
|
-
return [];
|
|
191
|
+
return _chunkLOSVFO56cjs.toPaginatedListResult.call(void 0, []);
|
|
138
192
|
}
|
|
139
193
|
throw err;
|
|
140
194
|
}
|
|
@@ -142,83 +196,6 @@ function createLocalFsStorageAdapter(options) {
|
|
|
142
196
|
};
|
|
143
197
|
}
|
|
144
198
|
|
|
145
|
-
// src/storage/memoryAdapter.ts
|
|
146
|
-
function createMemoryStorageAdapter(options = {}) {
|
|
147
|
-
const baseUrl = (options.baseUrl || "https://memory.local/uploads").replace(
|
|
148
|
-
/\/+$/,
|
|
149
|
-
""
|
|
150
|
-
);
|
|
151
|
-
const store = /* @__PURE__ */ new Map();
|
|
152
|
-
const toKey = (bucket, path2) => `${bucket}::${path2.replace(/^\/+/, "")}`;
|
|
153
|
-
const getPublicUrl = (bucket, path2) => {
|
|
154
|
-
const cleanPath = path2.replace(/^\/+/, "");
|
|
155
|
-
return `${baseUrl}/${bucket}/${cleanPath}`;
|
|
156
|
-
};
|
|
157
|
-
return {
|
|
158
|
-
async upload(params) {
|
|
159
|
-
const cleanPath = params.path.replace(/^\/+/, "");
|
|
160
|
-
const key = toKey(params.bucket, cleanPath);
|
|
161
|
-
if (params.upsert === false && store.has(key)) {
|
|
162
|
-
throw new Error(`Object already exists at ${params.bucket}/${cleanPath}`);
|
|
163
|
-
}
|
|
164
|
-
store.set(key, {
|
|
165
|
-
bucket: params.bucket,
|
|
166
|
-
path: cleanPath,
|
|
167
|
-
data: new Uint8Array(params.data),
|
|
168
|
-
contentType: params.contentType,
|
|
169
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
170
|
-
metadata: params.metadata
|
|
171
|
-
});
|
|
172
|
-
return {
|
|
173
|
-
bucket: params.bucket,
|
|
174
|
-
path: cleanPath,
|
|
175
|
-
url: getPublicUrl(params.bucket, cleanPath)
|
|
176
|
-
};
|
|
177
|
-
},
|
|
178
|
-
async delete(bucket, path2) {
|
|
179
|
-
const key = toKey(bucket, path2);
|
|
180
|
-
store.delete(key);
|
|
181
|
-
},
|
|
182
|
-
getPublicUrl,
|
|
183
|
-
async list(bucket, folder) {
|
|
184
|
-
const cleanFolder = folder ? folder.replace(/^\/+|\/+$/g, "") : "";
|
|
185
|
-
const entries = [];
|
|
186
|
-
for (const obj of store.values()) {
|
|
187
|
-
if (obj.bucket !== bucket) continue;
|
|
188
|
-
if (cleanFolder && !obj.path.startsWith(`${cleanFolder}/`)) continue;
|
|
189
|
-
const name = cleanFolder ? obj.path.slice(cleanFolder.length + 1) : obj.path;
|
|
190
|
-
entries.push({
|
|
191
|
-
name,
|
|
192
|
-
id: `${obj.bucket}/${obj.path}`,
|
|
193
|
-
createdAt: obj.createdAt,
|
|
194
|
-
size: obj.data.byteLength
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
return entries;
|
|
198
|
-
},
|
|
199
|
-
async deleteByUrl(url) {
|
|
200
|
-
const prefix = `${baseUrl}/`;
|
|
201
|
-
if (!url.startsWith(prefix)) return false;
|
|
202
|
-
const rest = url.slice(prefix.length);
|
|
203
|
-
const slashIdx = rest.indexOf("/");
|
|
204
|
-
if (slashIdx === -1) return false;
|
|
205
|
-
const bucket = rest.slice(0, slashIdx);
|
|
206
|
-
const filePath = rest.slice(slashIdx + 1);
|
|
207
|
-
const key = toKey(bucket, filePath);
|
|
208
|
-
return store.delete(key);
|
|
209
|
-
},
|
|
210
|
-
getObject(bucket, path2) {
|
|
211
|
-
return store.get(toKey(bucket, path2));
|
|
212
|
-
},
|
|
213
|
-
clear() {
|
|
214
|
-
store.clear();
|
|
215
|
-
},
|
|
216
|
-
get size() {
|
|
217
|
-
return store.size;
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
|
|
222
199
|
// src/storage/s3Adapter.ts
|
|
223
200
|
function normalizeS3Key(rawPath) {
|
|
224
201
|
const normalized = rawPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
@@ -231,6 +208,13 @@ function resolveTransport(options) {
|
|
|
231
208
|
if ("putObject" in options && typeof options.putObject === "function") return options;
|
|
232
209
|
throw new Error("S3StorageAdapter requires putObject/deleteObject or client/transport");
|
|
233
210
|
}
|
|
211
|
+
function isS3PreconditionError(err) {
|
|
212
|
+
if (!err || typeof err !== "object") return false;
|
|
213
|
+
const e = err;
|
|
214
|
+
const status = _nullishCoalesce(_optionalChain([e, 'access', _5 => _5.$metadata, 'optionalAccess', _6 => _6.httpStatusCode]), () => ( Number(e.statusCode)));
|
|
215
|
+
const code = String(_nullishCoalesce(_nullishCoalesce(e.code, () => ( e.name)), () => ( ""))).toLowerCase();
|
|
216
|
+
return status === 412 || code === "preconditionfailed" || code === "eexist";
|
|
217
|
+
}
|
|
234
218
|
function createS3StorageAdapter(options) {
|
|
235
219
|
const transport = resolveTransport(options);
|
|
236
220
|
const includeBucket = options.includeBucketInUrl !== false;
|
|
@@ -243,19 +227,34 @@ function createS3StorageAdapter(options) {
|
|
|
243
227
|
return {
|
|
244
228
|
async upload(params) {
|
|
245
229
|
const key = normalizeS3Key(params.path);
|
|
246
|
-
|
|
230
|
+
_chunkLOSVFO56cjs.validateStorageMetadata.call(void 0, params.metadata);
|
|
231
|
+
const isImmutable = params.upsert === false && params.contentType.includes("image/avif");
|
|
232
|
+
const cacheControl = _nullishCoalesce(_nullishCoalesce(params.cacheControl, () => ( options.cacheControl)), () => ( (isImmutable ? "public, max-age=31536000, immutable" : "public, max-age=2592000, must-revalidate")));
|
|
247
233
|
const metadata = params.metadata || params.contentHash ? {
|
|
248
|
-
...params.
|
|
249
|
-
...params.
|
|
234
|
+
...params.metadata,
|
|
235
|
+
...params.contentHash ? { contentHash: params.contentHash } : {}
|
|
250
236
|
} : void 0;
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
237
|
+
try {
|
|
238
|
+
await transport.putObject({
|
|
239
|
+
Bucket: params.bucket,
|
|
240
|
+
Key: key,
|
|
241
|
+
Body: params.data,
|
|
242
|
+
ContentType: params.contentType,
|
|
243
|
+
...cacheControl ? { CacheControl: cacheControl } : {},
|
|
244
|
+
...metadata ? { Metadata: metadata } : {},
|
|
245
|
+
...params.upsert === false ? { IfNoneMatch: "*" } : {}
|
|
246
|
+
});
|
|
247
|
+
} catch (err) {
|
|
248
|
+
if (isS3PreconditionError(err)) {
|
|
249
|
+
const existsErr = new Error(
|
|
250
|
+
`Object already exists at ${params.bucket}/${key}`
|
|
251
|
+
);
|
|
252
|
+
existsErr.code = "EEXIST";
|
|
253
|
+
existsErr.statusCode = 412;
|
|
254
|
+
throw existsErr;
|
|
255
|
+
}
|
|
256
|
+
throw err;
|
|
257
|
+
}
|
|
259
258
|
return {
|
|
260
259
|
bucket: params.bucket,
|
|
261
260
|
path: key,
|
|
@@ -265,17 +264,34 @@ function createS3StorageAdapter(options) {
|
|
|
265
264
|
async delete(bucket, path2) {
|
|
266
265
|
await transport.deleteObject({ Bucket: bucket, Key: normalizeS3Key(path2) });
|
|
267
266
|
},
|
|
267
|
+
async exists(bucket, path2) {
|
|
268
|
+
const key = normalizeS3Key(path2);
|
|
269
|
+
if (transport.headObject) {
|
|
270
|
+
try {
|
|
271
|
+
await transport.headObject({ Bucket: bucket, Key: key });
|
|
272
|
+
return true;
|
|
273
|
+
} catch (e5) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
const lastSlash = key.lastIndexOf("/");
|
|
278
|
+
const folder = lastSlash > 0 ? key.slice(0, lastSlash) : void 0;
|
|
279
|
+
const targetName = lastSlash > 0 ? key.slice(lastSlash + 1) : key;
|
|
280
|
+
const listed = await this.list(bucket, folder);
|
|
281
|
+
return listed.some((item) => item.name === targetName);
|
|
282
|
+
},
|
|
268
283
|
getPublicUrl,
|
|
269
|
-
async list(bucket, folder) {
|
|
284
|
+
async list(bucket, folder, listOptions) {
|
|
270
285
|
const cleanFolder = folder ? normalizeS3Key(folder).replace(/\/+$/, "") : "";
|
|
271
286
|
const prefix = cleanFolder ? `${cleanFolder}/` : void 0;
|
|
272
287
|
const input = {
|
|
273
288
|
Bucket: bucket,
|
|
274
289
|
...prefix ? { Prefix: prefix } : {},
|
|
275
|
-
MaxKeys: 1e3
|
|
290
|
+
MaxKeys: _nullishCoalesce(_optionalChain([listOptions, 'optionalAccess', _7 => _7.limit]), () => ( 1e3)),
|
|
291
|
+
..._optionalChain([listOptions, 'optionalAccess', _8 => _8.cursor]) ? { ContinuationToken: listOptions.cursor } : {}
|
|
276
292
|
};
|
|
277
293
|
const output = transport.listObjects ? await transport.listObjects(input) : transport.listObjectsV2 ? await transport.listObjectsV2(input) : void 0;
|
|
278
|
-
|
|
294
|
+
const entries = (_nullishCoalesce(_optionalChain([output, 'optionalAccess', _9 => _9.Contents]), () => ( []))).filter((item) => Boolean(item.Key)).map((item) => {
|
|
279
295
|
const name = prefix && item.Key.startsWith(prefix) ? item.Key.slice(prefix.length) : item.Key;
|
|
280
296
|
const createdAt = item.LastModified instanceof Date ? item.LastModified.toISOString() : item.LastModified;
|
|
281
297
|
return {
|
|
@@ -285,85 +301,67 @@ function createS3StorageAdapter(options) {
|
|
|
285
301
|
...item.Size !== void 0 ? { size: item.Size } : {}
|
|
286
302
|
};
|
|
287
303
|
}).filter((entry) => entry.name.length > 0);
|
|
304
|
+
return _chunkLOSVFO56cjs.toPaginatedListResult.call(void 0, entries, _optionalChain([output, 'optionalAccess', _10 => _10.NextContinuationToken]));
|
|
288
305
|
},
|
|
289
|
-
|
|
290
|
-
if (typeof options.publicBaseUrl !== "string" || !url) return
|
|
306
|
+
parseUrl(url) {
|
|
307
|
+
if (typeof options.publicBaseUrl !== "string" || !url) return null;
|
|
291
308
|
const prefix = `${options.publicBaseUrl.replace(/\/+$/, "")}/`;
|
|
292
309
|
const cleanUrl = url.split(/[?#]/)[0];
|
|
293
|
-
if (!cleanUrl.startsWith(prefix)) return
|
|
310
|
+
if (!cleanUrl.startsWith(prefix)) return null;
|
|
294
311
|
try {
|
|
295
312
|
const rest = decodeURIComponent(cleanUrl.slice(prefix.length)).replace(/^\/+/, "");
|
|
296
|
-
if (!rest) return
|
|
313
|
+
if (!rest) return null;
|
|
297
314
|
if (includeBucket) {
|
|
298
315
|
const slashIdx = rest.indexOf("/");
|
|
299
|
-
if (slashIdx <= 0 || slashIdx === rest.length - 1) return
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}
|
|
304
|
-
return true;
|
|
316
|
+
if (slashIdx <= 0 || slashIdx === rest.length - 1) return null;
|
|
317
|
+
return {
|
|
318
|
+
bucket: rest.slice(0, slashIdx),
|
|
319
|
+
path: rest.slice(slashIdx + 1)
|
|
320
|
+
};
|
|
305
321
|
}
|
|
306
|
-
if (!options.defaultBucket) return
|
|
307
|
-
|
|
322
|
+
if (!options.defaultBucket) return null;
|
|
323
|
+
return { bucket: options.defaultBucket, path: rest };
|
|
324
|
+
} catch (e6) {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
async deleteByUrl(url) {
|
|
329
|
+
const parsed = _optionalChain([this, 'access', _11 => _11.parseUrl, 'optionalCall', _12 => _12(url)]);
|
|
330
|
+
if (!parsed || !parsed.bucket) return false;
|
|
331
|
+
try {
|
|
332
|
+
await transport.deleteObject({
|
|
333
|
+
Bucket: parsed.bucket,
|
|
334
|
+
Key: parsed.path
|
|
335
|
+
});
|
|
308
336
|
return true;
|
|
309
|
-
} catch {
|
|
337
|
+
} catch (e7) {
|
|
310
338
|
return false;
|
|
311
339
|
}
|
|
312
340
|
}
|
|
313
341
|
};
|
|
314
342
|
}
|
|
315
343
|
|
|
316
|
-
// src/storage/urlParser.ts
|
|
317
|
-
var DEFAULT_PUBLIC_STORAGE_PREFIX = "/storage/v1/object/public/";
|
|
318
|
-
function parsePublicStorageUrl(url, publicPrefix = DEFAULT_PUBLIC_STORAGE_PREFIX) {
|
|
319
|
-
if (!url) return null;
|
|
320
|
-
try {
|
|
321
|
-
const parsed = new URL(url, "http://localhost");
|
|
322
|
-
const pathname = parsed.pathname;
|
|
323
|
-
const prefixIndex = pathname.indexOf(publicPrefix);
|
|
324
|
-
if (prefixIndex === -1) {
|
|
325
|
-
return null;
|
|
326
|
-
}
|
|
327
|
-
const relativePath = pathname.slice(prefixIndex + publicPrefix.length).replace(/^\/+/, "");
|
|
328
|
-
const segments = relativePath.split("/").filter(Boolean);
|
|
329
|
-
if (segments.length < 2) {
|
|
330
|
-
return null;
|
|
331
|
-
}
|
|
332
|
-
const [bucket, ...pathParts] = segments;
|
|
333
|
-
const filePath = pathParts.join("/");
|
|
334
|
-
const filename = pathParts[pathParts.length - 1];
|
|
335
|
-
const lastDotIndex = filename.lastIndexOf(".");
|
|
336
|
-
const extension = lastDotIndex >= 0 ? filename.slice(lastDotIndex) : null;
|
|
337
|
-
return {
|
|
338
|
-
bucket,
|
|
339
|
-
path: filePath,
|
|
340
|
-
filename,
|
|
341
|
-
extension
|
|
342
|
-
};
|
|
343
|
-
} catch {
|
|
344
|
-
return null;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
344
|
// src/storage/supabaseAdapter.ts
|
|
349
345
|
function createSupabaseStorageAdapter(client, options = {}) {
|
|
350
|
-
const publicPrefix = options.publicUrlPrefix || DEFAULT_PUBLIC_STORAGE_PREFIX;
|
|
346
|
+
const publicPrefix = options.publicUrlPrefix || _chunkQHU7FUO4cjs.DEFAULT_PUBLIC_STORAGE_PREFIX;
|
|
351
347
|
return {
|
|
352
348
|
async upload(params) {
|
|
353
|
-
|
|
349
|
+
_chunkLOSVFO56cjs.validateStorageMetadata.call(void 0, params.metadata);
|
|
350
|
+
const isImmutable = params.upsert === false && params.contentType.includes("image/avif");
|
|
351
|
+
const cacheControl = params.cacheControl || (isImmutable ? "public, max-age=31536000, immutable" : "public, max-age=2592000, must-revalidate");
|
|
354
352
|
const { data, error } = await client.storage.from(params.bucket).upload(params.path, params.data, {
|
|
355
353
|
contentType: params.contentType,
|
|
356
354
|
upsert: params.upsert === void 0 ? true : params.upsert,
|
|
357
355
|
cacheControl,
|
|
358
356
|
metadata: {
|
|
357
|
+
...params.metadata,
|
|
359
358
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
360
|
-
contentHash: params.contentHash || "unknown"
|
|
361
|
-
...params.metadata
|
|
359
|
+
contentHash: params.contentHash || "unknown"
|
|
362
360
|
}
|
|
363
361
|
});
|
|
364
362
|
if (error || !data) {
|
|
365
363
|
throw new Error(
|
|
366
|
-
`Failed to upload to Supabase storage (bucket: ${params.bucket}, path: ${params.path}): ${error
|
|
364
|
+
`Failed to upload to Supabase storage (bucket: ${params.bucket}, path: ${params.path}): ${_optionalChain([error, 'optionalAccess', _13 => _13.message]) || "Unknown error"}`
|
|
367
365
|
);
|
|
368
366
|
}
|
|
369
367
|
const {
|
|
@@ -381,60 +379,69 @@ function createSupabaseStorageAdapter(client, options = {}) {
|
|
|
381
379
|
throw new Error(`Failed to delete from Supabase storage: ${error.message}`);
|
|
382
380
|
}
|
|
383
381
|
},
|
|
382
|
+
async exists(bucket, path2) {
|
|
383
|
+
const cleanPath = path2.replace(/^\/+/, "");
|
|
384
|
+
const lastSlash = cleanPath.lastIndexOf("/");
|
|
385
|
+
const folder = lastSlash > 0 ? cleanPath.slice(0, lastSlash) : void 0;
|
|
386
|
+
const targetName = lastSlash > 0 ? cleanPath.slice(lastSlash + 1) : cleanPath;
|
|
387
|
+
const entries = await this.list(bucket, folder);
|
|
388
|
+
return entries.some((entry) => entry.name === targetName);
|
|
389
|
+
},
|
|
384
390
|
getPublicUrl(bucket, path2) {
|
|
385
391
|
const {
|
|
386
392
|
data: { publicUrl }
|
|
387
393
|
} = client.storage.from(bucket).getPublicUrl(path2);
|
|
388
394
|
return publicUrl;
|
|
389
395
|
},
|
|
390
|
-
async list(bucket, folder) {
|
|
396
|
+
async list(bucket, folder, listOptions) {
|
|
391
397
|
const bucketClient = client.storage.from(bucket);
|
|
392
398
|
if (!bucketClient.list) {
|
|
393
|
-
return [];
|
|
399
|
+
return _chunkLOSVFO56cjs.toPaginatedListResult.call(void 0, []);
|
|
394
400
|
}
|
|
401
|
+
const limit = _nullishCoalesce(_optionalChain([listOptions, 'optionalAccess', _14 => _14.limit]), () => ( 1e3));
|
|
402
|
+
const offset = _optionalChain([listOptions, 'optionalAccess', _15 => _15.cursor]) ? Number(listOptions.cursor) || 0 : 0;
|
|
395
403
|
const { data, error } = await bucketClient.list(folder, {
|
|
396
|
-
limit
|
|
404
|
+
limit,
|
|
405
|
+
...offset > 0 ? { offset } : {},
|
|
397
406
|
sortBy: { column: "created_at", order: "desc" }
|
|
398
407
|
});
|
|
399
408
|
if (error) {
|
|
400
409
|
throw new Error(`Failed to list Supabase bucket files: ${error.message}`);
|
|
401
410
|
}
|
|
402
|
-
|
|
411
|
+
const entries = (data || []).filter((f) => f.id !== null).map((f) => ({
|
|
403
412
|
name: f.name,
|
|
404
|
-
id: f.id
|
|
405
|
-
createdAt: f.created_at
|
|
413
|
+
id: _nullishCoalesce(f.id, () => ( void 0)),
|
|
414
|
+
createdAt: _nullishCoalesce(f.created_at, () => ( void 0))
|
|
406
415
|
}));
|
|
416
|
+
const nextCursor = _optionalChain([listOptions, 'optionalAccess', _16 => _16.limit]) !== void 0 && entries.length >= limit ? String(offset + entries.length) : void 0;
|
|
417
|
+
return _chunkLOSVFO56cjs.toPaginatedListResult.call(void 0, entries, nextCursor);
|
|
418
|
+
},
|
|
419
|
+
parseUrl(url) {
|
|
420
|
+
const info = _chunkQHU7FUO4cjs.parsePublicStorageUrl.call(void 0, url, publicPrefix);
|
|
421
|
+
if (!info) return null;
|
|
422
|
+
return { bucket: info.bucket, path: info.path };
|
|
407
423
|
},
|
|
408
424
|
async deleteByUrl(url) {
|
|
409
|
-
const info = parsePublicStorageUrl(url, publicPrefix);
|
|
425
|
+
const info = _chunkQHU7FUO4cjs.parsePublicStorageUrl.call(void 0, url, publicPrefix);
|
|
410
426
|
if (!info) return false;
|
|
411
427
|
try {
|
|
412
|
-
const
|
|
413
|
-
if (info.extension?.toLowerCase() === ".avif") {
|
|
414
|
-
const basePath = info.path.slice(0, -info.extension.length);
|
|
415
|
-
targets.add(`${basePath}-blur${info.extension}`);
|
|
416
|
-
targets.add(`${basePath}-thumb${info.extension}`);
|
|
417
|
-
}
|
|
418
|
-
const { error } = await client.storage.from(info.bucket).remove([...targets]);
|
|
428
|
+
const { error } = await client.storage.from(info.bucket).remove([info.path]);
|
|
419
429
|
return !error;
|
|
420
|
-
} catch {
|
|
430
|
+
} catch (e8) {
|
|
421
431
|
return false;
|
|
422
432
|
}
|
|
423
433
|
}
|
|
424
434
|
};
|
|
425
435
|
}
|
|
426
436
|
|
|
427
|
-
// src/storage/types.ts
|
|
428
|
-
function createStorageAdapter(adapter) {
|
|
429
|
-
return adapter;
|
|
430
|
-
}
|
|
431
437
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
exports.DEFAULT_PUBLIC_STORAGE_PREFIX = _chunkQHU7FUO4cjs.DEFAULT_PUBLIC_STORAGE_PREFIX; exports.createLocalFsStorageAdapter = createLocalFsStorageAdapter; exports.createMemoryStorageAdapter = _chunkLOSVFO56cjs.createMemoryStorageAdapter; exports.createS3StorageAdapter = createS3StorageAdapter; exports.createStorageAdapter = _chunkLOSVFO56cjs.createStorageAdapter; exports.createSupabaseStorageAdapter = createSupabaseStorageAdapter; exports.parsePublicStorageUrl = _chunkQHU7FUO4cjs.parsePublicStorageUrl; exports.toPaginatedListResult = _chunkLOSVFO56cjs.toPaginatedListResult; exports.validateStorageMetadata = _chunkLOSVFO56cjs.validateStorageMetadata;
|
|
440
447
|
//# sourceMappingURL=storage.cjs.map
|