@edgestore/react 0.6.0-canary.2 → 0.6.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.mjs CHANGED
@@ -1,464 +1 @@
1
- import * as React from 'react';
2
- import { EdgeStoreApiClientError } from '@edgestore/shared';
3
- import { U as UploadAbortedError } from './uploadAbortedError-e1379bb0.mjs';
4
-
5
- class EdgeStoreClientError extends Error {
6
- constructor(message){
7
- super(message);
8
- this.name = 'EdgeStoreError';
9
- }
10
- }
11
-
12
- async function handleError(res) {
13
- let json = {};
14
- try {
15
- json = await res.json();
16
- } catch (err) {
17
- throw new EdgeStoreClientError(`Failed to parse response. Make sure the api is correctly configured at ${res.url}`);
18
- }
19
- throw new EdgeStoreApiClientError({
20
- response: json
21
- });
22
- }
23
-
24
- function createNextProxy({ apiPath, uploadingCountRef, maxConcurrentUploads = 5, disableDevProxy }) {
25
- return new Proxy({}, {
26
- get (_, prop) {
27
- const bucketName = prop;
28
- const bucketFunctions = {
29
- upload: async (params)=>{
30
- try {
31
- params.onProgressChange?.(0);
32
- // This handles the case where the user cancels the upload while it's waiting in the queue
33
- const abortPromise = new Promise((resolve)=>{
34
- params.signal?.addEventListener('abort', ()=>{
35
- resolve();
36
- }, {
37
- once: true
38
- });
39
- });
40
- while(uploadingCountRef.current >= maxConcurrentUploads && uploadingCountRef.current > 0){
41
- await Promise.race([
42
- new Promise((resolve)=>setTimeout(resolve, 300)),
43
- abortPromise
44
- ]);
45
- if (params.signal?.aborted) {
46
- throw new UploadAbortedError('File upload aborted');
47
- }
48
- }
49
- uploadingCountRef.current++;
50
- const fileInfo = await uploadFile(params, {
51
- bucketName: bucketName,
52
- apiPath
53
- }, disableDevProxy);
54
- return fileInfo;
55
- } finally{
56
- uploadingCountRef.current--;
57
- }
58
- },
59
- confirmUpload: async (params)=>{
60
- const { success } = await confirmUpload(params, {
61
- bucketName: bucketName,
62
- apiPath
63
- });
64
- if (!success) {
65
- throw new EdgeStoreClientError('Failed to confirm upload');
66
- }
67
- },
68
- delete: async (params)=>{
69
- const { success } = await deleteFile(params, {
70
- bucketName: bucketName,
71
- apiPath
72
- });
73
- if (!success) {
74
- throw new EdgeStoreClientError('Failed to delete file');
75
- }
76
- }
77
- };
78
- return bucketFunctions;
79
- }
80
- });
81
- }
82
- async function uploadFile({ file, signal, input, onProgressChange, options }, { apiPath, bucketName }, disableDevProxy) {
83
- try {
84
- onProgressChange?.(0);
85
- const res = await fetch(`${apiPath}/request-upload`, {
86
- method: 'POST',
87
- credentials: 'include',
88
- signal: signal,
89
- body: JSON.stringify({
90
- bucketName,
91
- input,
92
- fileInfo: {
93
- extension: file.name.split('.').pop(),
94
- type: file.type,
95
- size: file.size,
96
- fileName: options?.manualFileName,
97
- replaceTargetUrl: options?.replaceTargetUrl,
98
- temporary: options?.temporary
99
- }
100
- }),
101
- headers: {
102
- 'Content-Type': 'application/json'
103
- }
104
- });
105
- if (!res.ok) {
106
- await handleError(res);
107
- }
108
- const json = await res.json();
109
- if ('multipart' in json) {
110
- await multipartUpload({
111
- bucketName,
112
- multipartInfo: json.multipart,
113
- onProgressChange,
114
- signal,
115
- file,
116
- apiPath
117
- });
118
- } else if ('uploadUrl' in json) {
119
- // Single part upload
120
- // Upload the file to the signed URL and get the progress
121
- await uploadFileInner({
122
- file,
123
- uploadUrl: json.uploadUrl,
124
- onProgressChange,
125
- signal
126
- });
127
- } else {
128
- throw new EdgeStoreClientError('An error occurred');
129
- }
130
- return {
131
- url: getUrl(json.accessUrl, apiPath, disableDevProxy),
132
- thumbnailUrl: json.thumbnailUrl ? getUrl(json.thumbnailUrl, apiPath, disableDevProxy) : null,
133
- size: json.size,
134
- uploadedAt: new Date(json.uploadedAt),
135
- path: json.path,
136
- pathOrder: json.pathOrder,
137
- metadata: json.metadata
138
- };
139
- } catch (e) {
140
- if (e instanceof Error && e.name === 'AbortError') {
141
- throw new UploadAbortedError('File upload aborted');
142
- }
143
- onProgressChange?.(0);
144
- throw e;
145
- }
146
- }
147
- /**
148
- * Protected files need third-party cookies to work.
149
- * Since third party cookies don't work on localhost,
150
- * we need to proxy the file through the server.
151
- */ function getUrl(url, apiPath, disableDevProxy) {
152
- const mode = typeof process !== 'undefined' ? process.env.NODE_ENV : import.meta.env?.DEV ? 'development' : 'production';
153
- if (mode === 'development' && !url.includes('/_public/') && !disableDevProxy) {
154
- const proxyUrl = new URL(window.location.origin);
155
- proxyUrl.pathname = `${apiPath}/proxy-file`;
156
- proxyUrl.search = new URLSearchParams({
157
- url
158
- }).toString();
159
- return proxyUrl.toString();
160
- }
161
- return url;
162
- }
163
- async function uploadFileInner(props) {
164
- const { file, uploadUrl, onProgressChange, signal } = props;
165
- const promise = new Promise((resolve, reject)=>{
166
- if (signal?.aborted) {
167
- reject(new UploadAbortedError('File upload aborted'));
168
- return;
169
- }
170
- const request = new XMLHttpRequest();
171
- request.open('PUT', uploadUrl);
172
- // This is for Azure provider. Specifies the blob type
173
- request.setRequestHeader('x-ms-blob-type', 'BlockBlob');
174
- request.addEventListener('loadstart', ()=>{
175
- onProgressChange?.(0);
176
- });
177
- request.upload.addEventListener('progress', (e)=>{
178
- if (e.lengthComputable) {
179
- // 2 decimal progress
180
- const progress = Math.round(e.loaded / e.total * 10000) / 100;
181
- onProgressChange?.(progress);
182
- }
183
- });
184
- request.addEventListener('error', ()=>{
185
- reject(new Error('Error uploading file'));
186
- });
187
- request.addEventListener('abort', ()=>{
188
- reject(new UploadAbortedError('File upload aborted'));
189
- });
190
- request.addEventListener('loadend', ()=>{
191
- // Return the ETag header (needed to complete multipart upload)
192
- resolve(request.getResponseHeader('ETag'));
193
- });
194
- if (signal) {
195
- signal.addEventListener('abort', ()=>{
196
- request.abort();
197
- });
198
- }
199
- request.send(file);
200
- });
201
- return promise;
202
- }
203
- async function multipartUpload(params) {
204
- const { bucketName, multipartInfo, onProgressChange, file, signal, apiPath } = params;
205
- const { partSize, parts, totalParts, uploadId, key } = multipartInfo;
206
- const uploadingParts = [];
207
- const uploadPart = async (params)=>{
208
- const { part, chunk } = params;
209
- const { uploadUrl } = part;
210
- const eTag = await uploadFileInner({
211
- file: chunk,
212
- uploadUrl,
213
- signal,
214
- onProgressChange: (progress)=>{
215
- const uploadingPart = uploadingParts.find((p)=>p.partNumber === part.partNumber);
216
- if (uploadingPart) {
217
- uploadingPart.progress = progress;
218
- } else {
219
- uploadingParts.push({
220
- partNumber: part.partNumber,
221
- progress
222
- });
223
- }
224
- const totalProgress = Math.round(uploadingParts.reduce((acc, p)=>acc + p.progress * 100, 0) / totalParts) / 100;
225
- onProgressChange?.(totalProgress);
226
- }
227
- });
228
- if (!eTag) {
229
- throw new EdgeStoreClientError('Could not get ETag from multipart response');
230
- }
231
- return {
232
- partNumber: part.partNumber,
233
- eTag
234
- };
235
- };
236
- // Upload the parts in parallel
237
- const completedParts = await queuedPromises({
238
- items: parts.map((part)=>({
239
- part,
240
- chunk: file.slice((part.partNumber - 1) * partSize, part.partNumber * partSize)
241
- })),
242
- fn: uploadPart,
243
- maxParallel: 5,
244
- maxRetries: 10
245
- });
246
- // Complete multipart upload
247
- const res = await fetch(`${apiPath}/complete-multipart-upload`, {
248
- method: 'POST',
249
- credentials: 'include',
250
- body: JSON.stringify({
251
- bucketName,
252
- uploadId,
253
- key,
254
- parts: completedParts
255
- }),
256
- headers: {
257
- 'Content-Type': 'application/json'
258
- }
259
- });
260
- if (!res.ok) {
261
- await handleError(res);
262
- }
263
- }
264
- async function confirmUpload({ url }, { apiPath, bucketName }) {
265
- const res = await fetch(`${apiPath}/confirm-upload`, {
266
- method: 'POST',
267
- credentials: 'include',
268
- body: JSON.stringify({
269
- url,
270
- bucketName
271
- }),
272
- headers: {
273
- 'Content-Type': 'application/json'
274
- }
275
- });
276
- if (!res.ok) {
277
- await handleError(res);
278
- }
279
- return res.json();
280
- }
281
- async function deleteFile({ url }, { apiPath, bucketName }) {
282
- const res = await fetch(`${apiPath}/delete-file`, {
283
- method: 'POST',
284
- credentials: 'include',
285
- body: JSON.stringify({
286
- url,
287
- bucketName
288
- }),
289
- headers: {
290
- 'Content-Type': 'application/json'
291
- }
292
- });
293
- if (!res.ok) {
294
- await handleError(res);
295
- }
296
- return res.json();
297
- }
298
- async function queuedPromises({ items, fn, maxParallel, maxRetries = 0 }) {
299
- const results = new Array(items.length);
300
- const executeWithRetry = async (func, retries)=>{
301
- try {
302
- return await func();
303
- } catch (error) {
304
- if (error instanceof UploadAbortedError) {
305
- throw error;
306
- }
307
- if (retries > 0) {
308
- await new Promise((resolve)=>setTimeout(resolve, 5000));
309
- return executeWithRetry(func, retries - 1);
310
- } else {
311
- throw error;
312
- }
313
- }
314
- };
315
- const semaphore = {
316
- count: maxParallel,
317
- async wait () {
318
- // If we've reached our maximum concurrency, or it's the last item, wait
319
- while(this.count <= 0)await new Promise((resolve)=>setTimeout(resolve, 500));
320
- this.count--;
321
- },
322
- signal () {
323
- this.count++;
324
- }
325
- };
326
- const tasks = items.map((item, i)=>(async ()=>{
327
- await semaphore.wait();
328
- try {
329
- const result = await executeWithRetry(()=>fn(item), maxRetries);
330
- results[i] = result;
331
- } finally{
332
- semaphore.signal();
333
- }
334
- })());
335
- await Promise.all(tasks);
336
- return results;
337
- }
338
-
339
- const DEFAULT_BASE_URL = (typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_EDGE_STORE_BASE_URL : import.meta.env?.EDGE_STORE_BASE_URL) ?? 'https://files.edgestore.dev';
340
- function createEdgeStoreProvider(opts) {
341
- const EdgeStoreContext = /*#__PURE__*/ React.createContext(undefined);
342
- const EdgeStoreProvider = ({ children, basePath })=>{
343
- return EdgeStoreProviderInner({
344
- children,
345
- context: EdgeStoreContext,
346
- basePath,
347
- maxConcurrentUploads: opts?.maxConcurrentUploads,
348
- disableDevProxy: opts?.disableDevProxy
349
- });
350
- };
351
- function useEdgeStore() {
352
- if (!EdgeStoreContext) {
353
- throw new Error('React Context is unavailable in Server Components');
354
- }
355
- // @ts-expect-error - We know that the context value should not be undefined
356
- const value = React.useContext(EdgeStoreContext);
357
- if (!value && process.env.NODE_ENV !== 'production') {
358
- throw new Error('[edgestore]: `useEdgeStore` must be wrapped in a <EdgeStoreProvider />');
359
- }
360
- return value;
361
- }
362
- return {
363
- EdgeStoreProvider,
364
- useEdgeStore
365
- };
366
- }
367
- function EdgeStoreProviderInner({ children, context, basePath, maxConcurrentUploads, disableDevProxy }) {
368
- const apiPath = basePath ? `${basePath}` : '/api/edgestore';
369
- const [state, setState] = React.useState({
370
- loading: true,
371
- initialized: false,
372
- error: false
373
- });
374
- const uploadingCountRef = React.useRef(0);
375
- const initExecuted = React.useRef(false); // to make sure we don't run init twice
376
- React.useEffect(()=>{
377
- if (!initExecuted.current) {
378
- void init();
379
- }
380
- return ()=>{
381
- initExecuted.current = true;
382
- };
383
- // eslint-disable-next-line react-hooks/exhaustive-deps
384
- }, []);
385
- async function init() {
386
- try {
387
- setState({
388
- loading: true,
389
- initialized: false,
390
- error: false
391
- });
392
- const res = await fetch(`${apiPath}/init`, {
393
- method: 'POST',
394
- credentials: 'include'
395
- });
396
- if (res.ok) {
397
- const json = await res.json();
398
- // Only call _init API if provider is edgestore
399
- if (json.providerName === 'edgestore') {
400
- const innerRes = await fetch(`${DEFAULT_BASE_URL}/_init`, {
401
- method: 'GET',
402
- credentials: 'include',
403
- headers: {
404
- 'x-edgestore-token': json.token
405
- }
406
- });
407
- if (innerRes.ok) {
408
- // update state
409
- setState({
410
- loading: false,
411
- initialized: true,
412
- error: false
413
- });
414
- } else {
415
- setState({
416
- loading: false,
417
- initialized: false,
418
- error: true
419
- });
420
- throw new EdgeStoreClientError("Couldn't initialize EdgeStore.");
421
- }
422
- } else {
423
- // For non-edgestore providers, just update state without calling _init
424
- setState({
425
- loading: false,
426
- initialized: true,
427
- error: false
428
- });
429
- }
430
- } else {
431
- setState({
432
- loading: false,
433
- initialized: false,
434
- error: true
435
- });
436
- await handleError(res);
437
- }
438
- } catch (err) {
439
- setState({
440
- loading: false,
441
- initialized: false,
442
- error: true
443
- });
444
- throw err;
445
- }
446
- }
447
- async function reset() {
448
- await init();
449
- }
450
- return /*#__PURE__*/ React.createElement(React.Fragment, null, /*#__PURE__*/ React.createElement(context.Provider, {
451
- value: {
452
- edgestore: createNextProxy({
453
- apiPath,
454
- uploadingCountRef,
455
- maxConcurrentUploads,
456
- disableDevProxy
457
- }),
458
- reset,
459
- state
460
- }
461
- }, children));
462
- }
463
-
464
- export { createEdgeStoreProvider };
1
+ export { createEdgeStoreProvider } from './contextProvider.mjs';
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ class EdgeStoreClientError extends Error {
4
+ constructor(message){
5
+ super(message);
6
+ this.name = 'EdgeStoreError';
7
+ }
8
+ }
9
+
10
+ module.exports = EdgeStoreClientError;
@@ -0,0 +1,8 @@
1
+ class EdgeStoreClientError extends Error {
2
+ constructor(message){
3
+ super(message);
4
+ this.name = 'EdgeStoreError';
5
+ }
6
+ }
7
+
8
+ export { EdgeStoreClientError as default };
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ var shared = require('@edgestore/shared');
4
+ var EdgeStoreClientError = require('./EdgeStoreClientError.js');
5
+
6
+ async function handleError(res) {
7
+ let json = {};
8
+ try {
9
+ json = await res.json();
10
+ } catch (err) {
11
+ throw new EdgeStoreClientError(`Failed to parse response. Make sure the api is correctly configured at ${res.url}`);
12
+ }
13
+ throw new shared.EdgeStoreApiClientError({
14
+ response: json
15
+ });
16
+ }
17
+
18
+ exports.handleError = handleError;
@@ -0,0 +1,16 @@
1
+ import { EdgeStoreApiClientError as EdgeStoreApiClientError$1 } from '@edgestore/shared';
2
+ import EdgeStoreClientError from './EdgeStoreClientError.mjs';
3
+
4
+ async function handleError(res) {
5
+ let json = {};
6
+ try {
7
+ json = await res.json();
8
+ } catch (err) {
9
+ throw new EdgeStoreClientError(`Failed to parse response. Make sure the api is correctly configured at ${res.url}`);
10
+ }
11
+ throw new EdgeStoreApiClientError$1({
12
+ response: json
13
+ });
14
+ }
15
+
16
+ export { handleError };
@@ -5,4 +5,4 @@ class UploadAbortedError extends Error {
5
5
  }
6
6
  }
7
7
 
8
- export { UploadAbortedError as U };
8
+ export { UploadAbortedError };
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  var shared = require('@edgestore/shared');
6
4
 
7
5
  // TODO: delete this file on next major release (moved to "errors")
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  /**
6
4
  * This will add the necessary query param to the url
7
5
  * to make the browser download the file instead of opening it.
package/package.json CHANGED
@@ -1,13 +1,22 @@
1
1
  {
2
2
  "name": "@edgestore/react",
3
- "version": "0.6.0-canary.2",
3
+ "version": "0.6.0",
4
4
  "description": "Upload files with ease from React/Next.js",
5
5
  "homepage": "https://edgestore.dev",
6
- "repository": "https://github.com/edgestorejs/edgestore.git",
6
+ "license": "MIT",
7
+ "sideEffects": false,
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/edgestorejs/edgestore.git",
11
+ "directory": "packages/react"
12
+ },
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
7
16
  "author": "Ravi <me@ravi.com>",
8
17
  "main": "dist/index.js",
9
18
  "module": "dist/index.mjs",
10
- "typings": "dist/index.d.ts",
19
+ "types": "dist/index.d.ts",
11
20
  "keywords": [
12
21
  "react",
13
22
  "nodejs",
@@ -26,20 +35,20 @@
26
35
  "require": "./dist/index.js",
27
36
  "default": "./dist/index.js"
28
37
  },
29
- "./utils": {
30
- "import": "./dist/utils/index.mjs",
31
- "require": "./dist/utils/index.js",
32
- "default": "./dist/utils/index.js"
38
+ "./errors": {
39
+ "import": "./dist/errors/index.mjs",
40
+ "require": "./dist/errors/index.js",
41
+ "default": "./dist/errors/index.js"
33
42
  },
34
43
  "./shared": {
35
44
  "import": "./dist/shared/index.mjs",
36
45
  "require": "./dist/shared/index.js",
37
46
  "default": "./dist/shared/index.js"
38
47
  },
39
- "./errors": {
40
- "import": "./dist/errors/index.mjs",
41
- "require": "./dist/errors/index.js",
42
- "default": "./dist/errors/index.js"
48
+ "./utils": {
49
+ "import": "./dist/utils/index.mjs",
50
+ "require": "./dist/utils/index.js",
51
+ "default": "./dist/utils/index.js"
43
52
  }
44
53
  },
45
54
  "files": [
@@ -48,20 +57,16 @@
48
57
  "README.md",
49
58
  "LICENSE",
50
59
  "package.json",
51
- "utils",
52
- "shared",
53
60
  "errors",
54
- "!**/*.test.*"
61
+ "shared",
62
+ "utils",
63
+ "!**/*.test.*",
64
+ "!**/__tests__"
55
65
  ],
56
- "private": false,
57
- "publishConfig": {
58
- "access": "public"
59
- },
60
- "license": "MIT",
61
66
  "dependencies": {
62
67
  "@aws-sdk/client-s3": "^3.294.0",
63
68
  "@aws-sdk/s3-request-presigner": "^3.294.0",
64
- "@edgestore/shared": "0.6.0-canary.2",
69
+ "@edgestore/shared": "0.6.0",
65
70
  "@panva/hkdf": "^1.0.4",
66
71
  "cookie": "^0.7.0",
67
72
  "jose": "^4.13.1",
@@ -1,8 +0,0 @@
1
- class UploadAbortedError extends Error {
2
- constructor(message) {
3
- super(message);
4
- this.name = 'UploadAbortedError';
5
- }
6
- }
7
-
8
- export { UploadAbortedError as U };