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.
Files changed (111) hide show
  1. package/CHANGELOG.md +89 -74
  2. package/MIGRATION.md +192 -0
  3. package/README.md +243 -259
  4. package/dist/bin.js +1186 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/blur.cjs +40 -412
  7. package/dist/blur.cjs.map +1 -1
  8. package/dist/blur.d.cts +5 -5
  9. package/dist/blur.d.ts +5 -5
  10. package/dist/blur.js +39 -393
  11. package/dist/blur.js.map +1 -1
  12. package/dist/blurDataUtils-DveBRtqE.d.cts +43 -0
  13. package/dist/blurDataUtils-yx9Q6HlU.d.ts +43 -0
  14. package/dist/blurService-D5TpMNji.d.ts +83 -0
  15. package/dist/blurService-DacwylJx.d.cts +83 -0
  16. package/dist/chunk-25GYKQ6J.js +158 -0
  17. package/dist/chunk-25GYKQ6J.js.map +1 -0
  18. package/dist/chunk-34L4TEE3.cjs +226 -0
  19. package/dist/chunk-34L4TEE3.cjs.map +1 -0
  20. package/dist/chunk-DFCSN3X7.cjs +158 -0
  21. package/dist/chunk-DFCSN3X7.cjs.map +1 -0
  22. package/dist/chunk-EWVIL57J.js +294 -0
  23. package/dist/chunk-EWVIL57J.js.map +1 -0
  24. package/dist/chunk-FP5YIWG5.cjs +135 -0
  25. package/dist/chunk-FP5YIWG5.cjs.map +1 -0
  26. package/dist/chunk-HILELQGS.js +226 -0
  27. package/dist/chunk-HILELQGS.js.map +1 -0
  28. package/dist/chunk-IDLLCVGB.js +2370 -0
  29. package/dist/chunk-IDLLCVGB.js.map +1 -0
  30. package/dist/chunk-J6A44Y3W.js +514 -0
  31. package/dist/chunk-J6A44Y3W.js.map +1 -0
  32. package/dist/chunk-LOSVFO56.cjs +145 -0
  33. package/dist/chunk-LOSVFO56.cjs.map +1 -0
  34. package/dist/chunk-MBQJ6NVJ.cjs +294 -0
  35. package/dist/chunk-MBQJ6NVJ.cjs.map +1 -0
  36. package/dist/chunk-MHGQEBPW.js +145 -0
  37. package/dist/chunk-MHGQEBPW.js.map +1 -0
  38. package/dist/chunk-QHU7FUO4.cjs +37 -0
  39. package/dist/chunk-QHU7FUO4.cjs.map +1 -0
  40. package/dist/chunk-S2IFAWJK.js +37 -0
  41. package/dist/chunk-S2IFAWJK.js.map +1 -0
  42. package/dist/chunk-TA5Y64JM.cjs +2370 -0
  43. package/dist/chunk-TA5Y64JM.cjs.map +1 -0
  44. package/dist/chunk-VYVZVUNI.js +135 -0
  45. package/dist/chunk-VYVZVUNI.js.map +1 -0
  46. package/dist/chunk-XKQMCY5Z.cjs +514 -0
  47. package/dist/chunk-XKQMCY5Z.cjs.map +1 -0
  48. package/dist/cli.cjs +273 -76
  49. package/dist/cli.cjs.map +1 -1
  50. package/dist/cli.js +269 -73
  51. package/dist/cli.js.map +1 -1
  52. package/dist/headless.cjs +8 -1880
  53. package/dist/headless.cjs.map +1 -1
  54. package/dist/headless.d.cts +3 -1
  55. package/dist/headless.d.ts +3 -1
  56. package/dist/headless.js +25 -1886
  57. package/dist/headless.js.map +1 -1
  58. package/dist/index-BIdnqtW8.d.ts +234 -0
  59. package/dist/index-Brk_uHs6.d.cts +234 -0
  60. package/dist/{index-FVXXKf2v.d.ts → index-CPMlaQsC.d.ts} +76 -153
  61. package/dist/{index-FVXXKf2v.d.cts → index-DHINe-rA.d.cts} +76 -153
  62. package/dist/index.cjs +64 -532
  63. package/dist/index.cjs.map +1 -1
  64. package/dist/index.d.cts +7 -4
  65. package/dist/index.d.ts +7 -4
  66. package/dist/index.js +63 -504
  67. package/dist/index.js.map +1 -1
  68. package/dist/{memoryAdapter-CBu7_3Pp.d.ts → memoryAdapter-Ck0ccZbF.d.cts} +3 -2
  69. package/dist/{memoryAdapter-BJjyLR-1.d.cts → memoryAdapter-DxEKaG0b.d.ts} +3 -2
  70. package/dist/placeholders-C4ftwaII.d.ts +5 -0
  71. package/dist/placeholders-x_3rt65M.d.cts +5 -0
  72. package/dist/react.cjs +1952 -3257
  73. package/dist/react.cjs.map +1 -1
  74. package/dist/react.d.cts +188 -50
  75. package/dist/react.d.ts +188 -50
  76. package/dist/react.js +1763 -3013
  77. package/dist/react.js.map +1 -1
  78. package/dist/server-handler.cjs +14 -0
  79. package/dist/server-handler.cjs.map +1 -0
  80. package/dist/server-handler.d.cts +3 -0
  81. package/dist/server-handler.d.ts +3 -0
  82. package/dist/server-handler.js +14 -0
  83. package/dist/server-handler.js.map +1 -0
  84. package/dist/server.cjs +1096 -532
  85. package/dist/server.cjs.map +1 -1
  86. package/dist/server.d.cts +77 -154
  87. package/dist/server.d.ts +77 -154
  88. package/dist/server.js +1050 -448
  89. package/dist/server.js.map +1 -1
  90. package/dist/storage.cjs +239 -232
  91. package/dist/storage.cjs.map +1 -1
  92. package/dist/storage.d.cts +21 -5
  93. package/dist/storage.d.ts +21 -5
  94. package/dist/storage.js +227 -207
  95. package/dist/storage.js.map +1 -1
  96. package/dist/styles/animated-image.css +112 -0
  97. package/dist/styles/dropzone-and-tiles.css +247 -0
  98. package/dist/styles/modals-and-dock.css +248 -0
  99. package/dist/styles/primitives.css +197 -0
  100. package/dist/styles/quality-and-inputs.css +184 -0
  101. package/dist/styles/tokens.css +123 -0
  102. package/dist/styles.css +235 -220
  103. package/dist/types-BrP-o9nd.d.cts +85 -0
  104. package/dist/types-sZ3KoFQY.d.ts +85 -0
  105. package/dist/types-yShGNQnW.d.cts +61 -0
  106. package/dist/types-yShGNQnW.d.ts +61 -0
  107. package/dist/{types-CiLY0DwB.d.cts → upload-C5Trd6Dh.d.cts} +27 -53
  108. package/dist/{types-CiLY0DwB.d.ts → upload-C5Trd6Dh.d.ts} +27 -53
  109. package/package.json +92 -32
  110. package/dist/blurService-DqsnfPTd.d.cts +0 -172
  111. package/dist/blurService-DqsnfPTd.d.ts +0 -172
package/dist/storage.cjs CHANGED
@@ -1,60 +1,123 @@
1
- 'use strict';
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
- var fs__default = /*#__PURE__*/_interopDefault(fs);
9
- var path__default = /*#__PURE__*/_interopDefault(path);
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 parts = normalized.split("/").filter((segment) => segment && segment !== "." && segment !== "..");
15
- if (parts.length === 0) {
23
+ const rawSegments = normalized.split("/").filter(Boolean);
24
+ if (rawSegments.length === 0) {
16
25
  throw new Error("Invalid storage path");
17
26
  }
18
- return parts.join("/");
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 = path__default.default.resolve(options.rootDir);
22
- const baseUrl = (options.baseUrl ?? "/uploads").replace(/\/+$/, "");
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 = path__default.default.resolve(resolvedRoot, subPath);
29
- if (!diskPath.startsWith(resolvedRoot)) {
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
- if (params.upsert === false) {
45
- try {
46
- await fs__default.default.access(diskPath);
47
- throw new Error(
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
- } catch (err) {
51
- if (err instanceof Error && err.message.includes("File already exists")) {
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 fs__default.default.unlink(diskPath);
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
- if (!url || typeof url !== "string") return false;
149
+ const parsed = parseUrl(url);
150
+ if (!parsed) return false;
77
151
  try {
78
- const cleanUrl = url.split(/[?#]/)[0];
79
- const prefix = `${baseUrl}/`;
80
- let subPath = null;
81
- if (cleanUrl.startsWith(prefix)) {
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 = path__default.default.resolve(
164
+ const targetDir = _path2.default.resolve(
113
165
  resolvedRoot,
114
166
  includeBucket ? cleanBucket : "",
115
167
  cleanFolder
116
168
  );
117
- if (!targetDir.startsWith(resolvedRoot)) {
118
- throw new Error("Path traversal blocked in LocalFsStorageAdapter");
119
- }
169
+ assertContainedInRoot(resolvedRoot, targetDir);
120
170
  try {
121
- const dirEntries = await fs__default.default.readdir(targetDir, { withFileTypes: true });
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 = path__default.default.join(targetDir, entry.name);
126
- const stat = await fs__default.default.stat(full);
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
- return results;
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
- const cacheControl = params.cacheControl ?? options.cacheControl ?? (params.contentType.includes("image/avif") ? "public, max-age=31536000, immutable" : "public, max-age=2592000, must-revalidate");
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.contentHash ? { contentHash: params.contentHash } : {},
249
- ...params.metadata
234
+ ...params.metadata,
235
+ ...params.contentHash ? { contentHash: params.contentHash } : {}
250
236
  } : void 0;
251
- await transport.putObject({
252
- Bucket: params.bucket,
253
- Key: key,
254
- Body: params.data,
255
- ContentType: params.contentType,
256
- ...cacheControl ? { CacheControl: cacheControl } : {},
257
- ...metadata ? { Metadata: metadata } : {}
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
- return (output?.Contents ?? []).filter((item) => Boolean(item.Key)).map((item) => {
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
- async deleteByUrl(url) {
290
- if (typeof options.publicBaseUrl !== "string" || !url) return false;
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 false;
310
+ if (!cleanUrl.startsWith(prefix)) return null;
294
311
  try {
295
312
  const rest = decodeURIComponent(cleanUrl.slice(prefix.length)).replace(/^\/+/, "");
296
- if (!rest) return false;
313
+ if (!rest) return null;
297
314
  if (includeBucket) {
298
315
  const slashIdx = rest.indexOf("/");
299
- if (slashIdx <= 0 || slashIdx === rest.length - 1) return false;
300
- await transport.deleteObject({
301
- Bucket: rest.slice(0, slashIdx),
302
- Key: rest.slice(slashIdx + 1)
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 false;
307
- await transport.deleteObject({ Bucket: options.defaultBucket, Key: rest });
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
- const cacheControl = params.cacheControl || (params.contentType.includes("image/avif") ? "public, max-age=31536000, immutable" : "public, max-age=2592000, must-revalidate");
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?.message || "Unknown 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: 1e3,
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
- return (data || []).filter((f) => f.id !== null).map((f) => ({
411
+ const entries = (data || []).filter((f) => f.id !== null).map((f) => ({
403
412
  name: f.name,
404
- id: f.id ?? void 0,
405
- createdAt: f.created_at ?? void 0
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 targets = /* @__PURE__ */ new Set([info.path]);
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
- exports.DEFAULT_PUBLIC_STORAGE_PREFIX = DEFAULT_PUBLIC_STORAGE_PREFIX;
433
- exports.createLocalFsStorageAdapter = createLocalFsStorageAdapter;
434
- exports.createMemoryStorageAdapter = createMemoryStorageAdapter;
435
- exports.createS3StorageAdapter = createS3StorageAdapter;
436
- exports.createStorageAdapter = createStorageAdapter;
437
- exports.createSupabaseStorageAdapter = createSupabaseStorageAdapter;
438
- exports.parsePublicStorageUrl = parsePublicStorageUrl;
439
- //# sourceMappingURL=storage.cjs.map
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