@edgestore/react 0.1.5-alpha.1 → 0.1.5-alpha.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/contextProvider.d.ts +21 -1
- package/dist/contextProvider.d.ts.map +1 -1
- package/dist/createNextProxy.d.ts +1 -30
- package/dist/createNextProxy.d.ts.map +1 -1
- package/dist/index.js +88 -21
- package/dist/index.mjs +88 -21
- package/dist/libs/errors/EdgeStoreClientError.d.ts +5 -0
- package/dist/libs/errors/EdgeStoreClientError.d.ts.map +1 -0
- package/dist/libs/errors/handleError.d.ts +2 -0
- package/dist/libs/errors/handleError.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/contextProvider.tsx +91 -12
- package/src/createNextProxy.ts +25 -39
- package/src/libs/errors/{EdgeStoreError.ts → EdgeStoreClientError.ts} +2 -2
- package/src/libs/errors/handleError.ts +13 -0
- package/dist/libs/errors/EdgeStoreError.d.ts +0 -5
- package/dist/libs/errors/EdgeStoreError.d.ts.map +0 -1
|
@@ -10,6 +10,13 @@ type EdgeStoreContextValue<TRouter extends AnyRouter> = {
|
|
|
10
10
|
* Can be used after a sign-in or sign-out, for example.
|
|
11
11
|
*/
|
|
12
12
|
reset: () => Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* The current state of the Edge Store provider.
|
|
15
|
+
*
|
|
16
|
+
* You can use this to wait for the provider to be initialized
|
|
17
|
+
* before trying to show private images on your app.
|
|
18
|
+
*/
|
|
19
|
+
state: ProviderState;
|
|
13
20
|
};
|
|
14
21
|
export declare function createEdgeStoreProvider<TRouter extends AnyRouter>(opts?: {
|
|
15
22
|
/**
|
|
@@ -31,8 +38,21 @@ export declare function createEdgeStoreProvider<TRouter extends AnyRouter>(opts?
|
|
|
31
38
|
* @example - If your app is hosted at `https://example.com/my-app`, you can set the `basePath` to `/my-app/api/edgestore`.
|
|
32
39
|
*/
|
|
33
40
|
basePath?: string | undefined;
|
|
34
|
-
}) => JSX.Element;
|
|
41
|
+
}) => React.JSX.Element;
|
|
35
42
|
useEdgeStore: () => EdgeStoreContextValue<TRouter>;
|
|
36
43
|
};
|
|
44
|
+
type ProviderState = {
|
|
45
|
+
loading: true;
|
|
46
|
+
initialized: false;
|
|
47
|
+
error: false;
|
|
48
|
+
} | {
|
|
49
|
+
loading: false;
|
|
50
|
+
initialized: false;
|
|
51
|
+
error: true;
|
|
52
|
+
} | {
|
|
53
|
+
loading: false;
|
|
54
|
+
initialized: true;
|
|
55
|
+
error: false;
|
|
56
|
+
};
|
|
37
57
|
export {};
|
|
38
58
|
//# sourceMappingURL=contextProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextProvider.d.ts","sourceRoot":"","sources":["../src/contextProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"contextProvider.d.ts","sourceRoot":"","sources":["../src/contextProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAU1E,KAAK,qBAAqB,CAAC,OAAO,SAAS,SAAS,IAAI;IACtD,SAAS,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;IACpC;;;;;OAKG;IACH,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B;;;;;OAKG;IACH,KAAK,EAAE,aAAa,CAAC;CACtB,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,OAAO,SAAS,SAAS,EAAE,IAAI,CAAC,EAAE;IACxE;;;;;;OAMG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;;kBASa,MAAM,SAAS;QACzB;;;;;;WAMG;;;;EAgCN;AAED,KAAK,aAAa,GACd;IACE,OAAO,EAAE,IAAI,CAAC;IACd,WAAW,EAAE,KAAK,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;CACd,GACD;IACE,OAAO,EAAE,KAAK,CAAC;IACf,WAAW,EAAE,KAAK,CAAC;IACnB,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,OAAO,EAAE,KAAK,CAAC;IACf,WAAW,EAAE,IAAI,CAAC;IAClB,KAAK,EAAE,KAAK,CAAC;CACd,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import { type AnyRouter, type InferBucketPathObject, type InferMetadataObject } from '@edgestore/server/core';
|
|
2
|
+
import { type AnyRouter, type InferBucketPathObject, type InferMetadataObject, type UploadOptions } from '@edgestore/server/core';
|
|
3
3
|
import { type z } from 'zod';
|
|
4
4
|
/**
|
|
5
5
|
* @internal
|
|
@@ -44,35 +44,6 @@ export type BucketFunctions<TRouter extends AnyRouter> = {
|
|
|
44
44
|
};
|
|
45
45
|
};
|
|
46
46
|
type OnProgressChangeHandler = (progress: number) => void;
|
|
47
|
-
type UploadOptions = {
|
|
48
|
-
/**
|
|
49
|
-
* e.g. 'my-file-name.jpg'
|
|
50
|
-
*
|
|
51
|
-
* By default, a unique file name will be generated for each upload.
|
|
52
|
-
* If you want to use a custom file name, you can use this option.
|
|
53
|
-
* If you use the same file name for multiple uploads, the previous file will be overwritten.
|
|
54
|
-
* But it might take some time for the CDN cache to be cleared.
|
|
55
|
-
* So maybe you will keep seeing the old file for a while.
|
|
56
|
-
*
|
|
57
|
-
* If you want to replace an existing file immediately leave the `manualFileName` option empty and use the `replaceTargetUrl` option.
|
|
58
|
-
*/
|
|
59
|
-
manualFileName?: string;
|
|
60
|
-
/**
|
|
61
|
-
* Use this to replace an existing file.
|
|
62
|
-
* It will automatically delete the existing file when the upload is complete.
|
|
63
|
-
*/
|
|
64
|
-
replaceTargetUrl?: string;
|
|
65
|
-
/**
|
|
66
|
-
* If true, the file needs to be confirmed by using the `confirmUpload` function.
|
|
67
|
-
* If the file is not confirmed within 24 hours, it will be deleted.
|
|
68
|
-
*
|
|
69
|
-
* This is useful for pages where the file is uploaded as soon as it is selected,
|
|
70
|
-
* but the user can leave the page without submitting the form.
|
|
71
|
-
*
|
|
72
|
-
* This avoids unnecessary zombie files in the bucket.
|
|
73
|
-
*/
|
|
74
|
-
temporary?: boolean;
|
|
75
|
-
};
|
|
76
47
|
export declare function createNextProxy<TRouter extends AnyRouter>({ apiPath, uploadingCountRef, maxConcurrentUploads, }: {
|
|
77
48
|
apiPath: string;
|
|
78
49
|
uploadingCountRef: React.MutableRefObject<number>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createNextProxy.d.ts","sourceRoot":"","sources":["../src/createNextProxy.ts"],"names":[],"mappings":";AACA,OAAO,EACL,KAAK,SAAS,EACd,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,
|
|
1
|
+
{"version":3,"file":"createNextProxy.d.ts","sourceRoot":"","sources":["../src/createNextProxy.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var React = require('react');
|
|
6
|
+
var core = require('@edgestore/server/core');
|
|
6
7
|
|
|
7
8
|
function _interopNamespace(e) {
|
|
8
9
|
if (e && e.__esModule) return e;
|
|
@@ -24,13 +25,24 @@ function _interopNamespace(e) {
|
|
|
24
25
|
|
|
25
26
|
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
26
27
|
|
|
27
|
-
class
|
|
28
|
+
class EdgeStoreClientError extends Error {
|
|
28
29
|
constructor(message){
|
|
29
30
|
super(message);
|
|
30
31
|
this.name = 'EdgeStoreError';
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
async function handleError(res) {
|
|
36
|
+
try {
|
|
37
|
+
const json = await res.json();
|
|
38
|
+
throw new core.EdgeStoreApiClientError({
|
|
39
|
+
response: json
|
|
40
|
+
});
|
|
41
|
+
} catch (err) {
|
|
42
|
+
throw new EdgeStoreClientError(`Failed to parse response. Make sure the api is correctly configured at ${res.url}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
34
46
|
function createNextProxy({ apiPath, uploadingCountRef, maxConcurrentUploads = 5 }) {
|
|
35
47
|
return new Proxy({}, {
|
|
36
48
|
get (_, prop) {
|
|
@@ -57,7 +69,7 @@ function createNextProxy({ apiPath, uploadingCountRef, maxConcurrentUploads = 5
|
|
|
57
69
|
apiPath
|
|
58
70
|
});
|
|
59
71
|
if (!success) {
|
|
60
|
-
throw new
|
|
72
|
+
throw new EdgeStoreClientError('Failed to confirm upload');
|
|
61
73
|
}
|
|
62
74
|
},
|
|
63
75
|
delete: async (params)=>{
|
|
@@ -66,7 +78,7 @@ function createNextProxy({ apiPath, uploadingCountRef, maxConcurrentUploads = 5
|
|
|
66
78
|
apiPath
|
|
67
79
|
});
|
|
68
80
|
if (!success) {
|
|
69
|
-
throw new
|
|
81
|
+
throw new EdgeStoreClientError('Failed to delete file');
|
|
70
82
|
}
|
|
71
83
|
}
|
|
72
84
|
};
|
|
@@ -95,6 +107,9 @@ async function uploadFile({ file, input, onProgressChange, options }, { apiPath,
|
|
|
95
107
|
'Content-Type': 'application/json'
|
|
96
108
|
}
|
|
97
109
|
});
|
|
110
|
+
if (!res.ok) {
|
|
111
|
+
await handleError(res);
|
|
112
|
+
}
|
|
98
113
|
const json = await res.json();
|
|
99
114
|
if ('multipart' in json) {
|
|
100
115
|
await multipartUpload({
|
|
@@ -109,7 +124,7 @@ async function uploadFile({ file, input, onProgressChange, options }, { apiPath,
|
|
|
109
124
|
// Upload the file to the signed URL and get the progress
|
|
110
125
|
await uploadFileInner(file, json.uploadUrl, onProgressChange);
|
|
111
126
|
} else {
|
|
112
|
-
throw new
|
|
127
|
+
throw new EdgeStoreClientError('An error occurred');
|
|
113
128
|
}
|
|
114
129
|
return {
|
|
115
130
|
url: getUrl(json.accessUrl, apiPath),
|
|
@@ -130,7 +145,8 @@ async function uploadFile({ file, input, onProgressChange, options }, { apiPath,
|
|
|
130
145
|
* Since third party cookies doesn't work on localhost,
|
|
131
146
|
* we need to proxy the file through the server.
|
|
132
147
|
*/ function getUrl(url, apiPath) {
|
|
133
|
-
|
|
148
|
+
const mode = process !== undefined ? process.env.NODE_ENV : undefined?.DEV ? 'development' : 'production';
|
|
149
|
+
if (mode === 'development' && !url.includes('/_public/')) {
|
|
134
150
|
const proxyUrl = new URL(window.location.origin);
|
|
135
151
|
proxyUrl.pathname = `${apiPath}/proxy-file`;
|
|
136
152
|
proxyUrl.search = new URLSearchParams({
|
|
@@ -144,6 +160,8 @@ const uploadFileInner = async (file, uploadUrl, onProgressChange)=>{
|
|
|
144
160
|
const promise = new Promise((resolve, reject)=>{
|
|
145
161
|
const request = new XMLHttpRequest();
|
|
146
162
|
request.open('PUT', uploadUrl);
|
|
163
|
+
// This is for Azure provider. Specifies the blob type
|
|
164
|
+
request.setRequestHeader('x-ms-blob-type', 'BlockBlob');
|
|
147
165
|
request.addEventListener('loadstart', ()=>{
|
|
148
166
|
onProgressChange?.(0);
|
|
149
167
|
});
|
|
@@ -189,7 +207,7 @@ async function multipartUpload(params) {
|
|
|
189
207
|
onProgressChange?.(totalProgress);
|
|
190
208
|
});
|
|
191
209
|
if (!eTag) {
|
|
192
|
-
throw new
|
|
210
|
+
throw new EdgeStoreClientError('Could not get ETag from multipart response');
|
|
193
211
|
}
|
|
194
212
|
return {
|
|
195
213
|
partNumber: part.partNumber,
|
|
@@ -220,7 +238,7 @@ async function multipartUpload(params) {
|
|
|
220
238
|
}
|
|
221
239
|
});
|
|
222
240
|
if (!res.ok) {
|
|
223
|
-
|
|
241
|
+
await handleError(res);
|
|
224
242
|
}
|
|
225
243
|
}
|
|
226
244
|
async function confirmUpload({ url }, { apiPath, bucketName }) {
|
|
@@ -235,7 +253,7 @@ async function confirmUpload({ url }, { apiPath, bucketName }) {
|
|
|
235
253
|
}
|
|
236
254
|
});
|
|
237
255
|
if (!res.ok) {
|
|
238
|
-
|
|
256
|
+
await handleError(res);
|
|
239
257
|
}
|
|
240
258
|
return res.json();
|
|
241
259
|
}
|
|
@@ -251,7 +269,7 @@ async function deleteFile({ url }, { apiPath, bucketName }) {
|
|
|
251
269
|
}
|
|
252
270
|
});
|
|
253
271
|
if (!res.ok) {
|
|
254
|
-
|
|
272
|
+
await handleError(res);
|
|
255
273
|
}
|
|
256
274
|
return res.json();
|
|
257
275
|
}
|
|
@@ -293,7 +311,7 @@ async function queuedPromises({ items, fn, maxParallel, maxRetries = 0 }) {
|
|
|
293
311
|
return results;
|
|
294
312
|
}
|
|
295
313
|
|
|
296
|
-
const DEFAULT_BASE_URL = process.env.NEXT_PUBLIC_EDGE_STORE_BASE_URL ?? 'https://files.edgestore.dev';
|
|
314
|
+
const DEFAULT_BASE_URL = (process !== undefined ? process.env.NEXT_PUBLIC_EDGE_STORE_BASE_URL : undefined?.EDGE_STORE_BASE_URL) ?? 'https://files.edgestore.dev';
|
|
297
315
|
function createEdgeStoreProvider(opts) {
|
|
298
316
|
const EdgeStoreContext = /*#__PURE__*/ React__namespace.createContext(undefined);
|
|
299
317
|
const EdgeStoreProvider = ({ children, basePath })=>{
|
|
@@ -322,23 +340,71 @@ function createEdgeStoreProvider(opts) {
|
|
|
322
340
|
}
|
|
323
341
|
function EdgeStoreProviderInner({ children, context, basePath, maxConcurrentUploads }) {
|
|
324
342
|
const apiPath = basePath ? `${basePath}` : '/api/edgestore';
|
|
343
|
+
const [state, setState] = React__namespace.useState({
|
|
344
|
+
loading: true,
|
|
345
|
+
initialized: false,
|
|
346
|
+
error: false
|
|
347
|
+
});
|
|
325
348
|
const uploadingCountRef = React__namespace.useRef(0);
|
|
349
|
+
const initExecuted = React__namespace.useRef(false); // to make sure we don't run init twice
|
|
326
350
|
React__namespace.useEffect(()=>{
|
|
327
|
-
|
|
351
|
+
if (!initExecuted.current) {
|
|
352
|
+
void init();
|
|
353
|
+
}
|
|
354
|
+
return ()=>{
|
|
355
|
+
initExecuted.current = true;
|
|
356
|
+
};
|
|
328
357
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
329
358
|
}, []);
|
|
330
359
|
async function init() {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
360
|
+
try {
|
|
361
|
+
setState({
|
|
362
|
+
loading: true,
|
|
363
|
+
initialized: false,
|
|
364
|
+
error: false
|
|
365
|
+
});
|
|
366
|
+
const res = await fetch(`${apiPath}/init`, {
|
|
367
|
+
method: 'POST'
|
|
368
|
+
});
|
|
369
|
+
if (res.ok) {
|
|
370
|
+
const json = await res.json();
|
|
371
|
+
const innerRes = await fetch(`${DEFAULT_BASE_URL}/_init`, {
|
|
372
|
+
method: 'GET',
|
|
373
|
+
credentials: 'include',
|
|
374
|
+
headers: {
|
|
375
|
+
'x-edgestore-token': json.token
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
if (innerRes.ok) {
|
|
379
|
+
// update state
|
|
380
|
+
setState({
|
|
381
|
+
loading: false,
|
|
382
|
+
initialized: true,
|
|
383
|
+
error: false
|
|
384
|
+
});
|
|
385
|
+
} else {
|
|
386
|
+
setState({
|
|
387
|
+
loading: false,
|
|
388
|
+
initialized: false,
|
|
389
|
+
error: true
|
|
390
|
+
});
|
|
391
|
+
throw new EdgeStoreClientError("Couldn't initialize Edge Store.");
|
|
340
392
|
}
|
|
393
|
+
} else {
|
|
394
|
+
setState({
|
|
395
|
+
loading: false,
|
|
396
|
+
initialized: false,
|
|
397
|
+
error: true
|
|
398
|
+
});
|
|
399
|
+
await handleError(res);
|
|
400
|
+
}
|
|
401
|
+
} catch (err) {
|
|
402
|
+
setState({
|
|
403
|
+
loading: false,
|
|
404
|
+
initialized: false,
|
|
405
|
+
error: true
|
|
341
406
|
});
|
|
407
|
+
throw err;
|
|
342
408
|
}
|
|
343
409
|
}
|
|
344
410
|
async function reset() {
|
|
@@ -351,7 +417,8 @@ function EdgeStoreProviderInner({ children, context, basePath, maxConcurrentUplo
|
|
|
351
417
|
uploadingCountRef,
|
|
352
418
|
maxConcurrentUploads
|
|
353
419
|
}),
|
|
354
|
-
reset
|
|
420
|
+
reset,
|
|
421
|
+
state
|
|
355
422
|
}
|
|
356
423
|
}, children));
|
|
357
424
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { EdgeStoreApiClientError } from '@edgestore/server/core';
|
|
2
3
|
|
|
3
|
-
class
|
|
4
|
+
class EdgeStoreClientError extends Error {
|
|
4
5
|
constructor(message){
|
|
5
6
|
super(message);
|
|
6
7
|
this.name = 'EdgeStoreError';
|
|
7
8
|
}
|
|
8
9
|
}
|
|
9
10
|
|
|
11
|
+
async function handleError(res) {
|
|
12
|
+
try {
|
|
13
|
+
const json = await res.json();
|
|
14
|
+
throw new EdgeStoreApiClientError({
|
|
15
|
+
response: json
|
|
16
|
+
});
|
|
17
|
+
} catch (err) {
|
|
18
|
+
throw new EdgeStoreClientError(`Failed to parse response. Make sure the api is correctly configured at ${res.url}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
10
22
|
function createNextProxy({ apiPath, uploadingCountRef, maxConcurrentUploads = 5 }) {
|
|
11
23
|
return new Proxy({}, {
|
|
12
24
|
get (_, prop) {
|
|
@@ -33,7 +45,7 @@ function createNextProxy({ apiPath, uploadingCountRef, maxConcurrentUploads = 5
|
|
|
33
45
|
apiPath
|
|
34
46
|
});
|
|
35
47
|
if (!success) {
|
|
36
|
-
throw new
|
|
48
|
+
throw new EdgeStoreClientError('Failed to confirm upload');
|
|
37
49
|
}
|
|
38
50
|
},
|
|
39
51
|
delete: async (params)=>{
|
|
@@ -42,7 +54,7 @@ function createNextProxy({ apiPath, uploadingCountRef, maxConcurrentUploads = 5
|
|
|
42
54
|
apiPath
|
|
43
55
|
});
|
|
44
56
|
if (!success) {
|
|
45
|
-
throw new
|
|
57
|
+
throw new EdgeStoreClientError('Failed to delete file');
|
|
46
58
|
}
|
|
47
59
|
}
|
|
48
60
|
};
|
|
@@ -71,6 +83,9 @@ async function uploadFile({ file, input, onProgressChange, options }, { apiPath,
|
|
|
71
83
|
'Content-Type': 'application/json'
|
|
72
84
|
}
|
|
73
85
|
});
|
|
86
|
+
if (!res.ok) {
|
|
87
|
+
await handleError(res);
|
|
88
|
+
}
|
|
74
89
|
const json = await res.json();
|
|
75
90
|
if ('multipart' in json) {
|
|
76
91
|
await multipartUpload({
|
|
@@ -85,7 +100,7 @@ async function uploadFile({ file, input, onProgressChange, options }, { apiPath,
|
|
|
85
100
|
// Upload the file to the signed URL and get the progress
|
|
86
101
|
await uploadFileInner(file, json.uploadUrl, onProgressChange);
|
|
87
102
|
} else {
|
|
88
|
-
throw new
|
|
103
|
+
throw new EdgeStoreClientError('An error occurred');
|
|
89
104
|
}
|
|
90
105
|
return {
|
|
91
106
|
url: getUrl(json.accessUrl, apiPath),
|
|
@@ -106,7 +121,8 @@ async function uploadFile({ file, input, onProgressChange, options }, { apiPath,
|
|
|
106
121
|
* Since third party cookies doesn't work on localhost,
|
|
107
122
|
* we need to proxy the file through the server.
|
|
108
123
|
*/ function getUrl(url, apiPath) {
|
|
109
|
-
|
|
124
|
+
const mode = process !== undefined ? process.env.NODE_ENV : import.meta.env?.DEV ? 'development' : 'production';
|
|
125
|
+
if (mode === 'development' && !url.includes('/_public/')) {
|
|
110
126
|
const proxyUrl = new URL(window.location.origin);
|
|
111
127
|
proxyUrl.pathname = `${apiPath}/proxy-file`;
|
|
112
128
|
proxyUrl.search = new URLSearchParams({
|
|
@@ -120,6 +136,8 @@ const uploadFileInner = async (file, uploadUrl, onProgressChange)=>{
|
|
|
120
136
|
const promise = new Promise((resolve, reject)=>{
|
|
121
137
|
const request = new XMLHttpRequest();
|
|
122
138
|
request.open('PUT', uploadUrl);
|
|
139
|
+
// This is for Azure provider. Specifies the blob type
|
|
140
|
+
request.setRequestHeader('x-ms-blob-type', 'BlockBlob');
|
|
123
141
|
request.addEventListener('loadstart', ()=>{
|
|
124
142
|
onProgressChange?.(0);
|
|
125
143
|
});
|
|
@@ -165,7 +183,7 @@ async function multipartUpload(params) {
|
|
|
165
183
|
onProgressChange?.(totalProgress);
|
|
166
184
|
});
|
|
167
185
|
if (!eTag) {
|
|
168
|
-
throw new
|
|
186
|
+
throw new EdgeStoreClientError('Could not get ETag from multipart response');
|
|
169
187
|
}
|
|
170
188
|
return {
|
|
171
189
|
partNumber: part.partNumber,
|
|
@@ -196,7 +214,7 @@ async function multipartUpload(params) {
|
|
|
196
214
|
}
|
|
197
215
|
});
|
|
198
216
|
if (!res.ok) {
|
|
199
|
-
|
|
217
|
+
await handleError(res);
|
|
200
218
|
}
|
|
201
219
|
}
|
|
202
220
|
async function confirmUpload({ url }, { apiPath, bucketName }) {
|
|
@@ -211,7 +229,7 @@ async function confirmUpload({ url }, { apiPath, bucketName }) {
|
|
|
211
229
|
}
|
|
212
230
|
});
|
|
213
231
|
if (!res.ok) {
|
|
214
|
-
|
|
232
|
+
await handleError(res);
|
|
215
233
|
}
|
|
216
234
|
return res.json();
|
|
217
235
|
}
|
|
@@ -227,7 +245,7 @@ async function deleteFile({ url }, { apiPath, bucketName }) {
|
|
|
227
245
|
}
|
|
228
246
|
});
|
|
229
247
|
if (!res.ok) {
|
|
230
|
-
|
|
248
|
+
await handleError(res);
|
|
231
249
|
}
|
|
232
250
|
return res.json();
|
|
233
251
|
}
|
|
@@ -269,7 +287,7 @@ async function queuedPromises({ items, fn, maxParallel, maxRetries = 0 }) {
|
|
|
269
287
|
return results;
|
|
270
288
|
}
|
|
271
289
|
|
|
272
|
-
const DEFAULT_BASE_URL = process.env.NEXT_PUBLIC_EDGE_STORE_BASE_URL ?? 'https://files.edgestore.dev';
|
|
290
|
+
const DEFAULT_BASE_URL = (process !== undefined ? process.env.NEXT_PUBLIC_EDGE_STORE_BASE_URL : import.meta.env?.EDGE_STORE_BASE_URL) ?? 'https://files.edgestore.dev';
|
|
273
291
|
function createEdgeStoreProvider(opts) {
|
|
274
292
|
const EdgeStoreContext = /*#__PURE__*/ React.createContext(undefined);
|
|
275
293
|
const EdgeStoreProvider = ({ children, basePath })=>{
|
|
@@ -298,23 +316,71 @@ function createEdgeStoreProvider(opts) {
|
|
|
298
316
|
}
|
|
299
317
|
function EdgeStoreProviderInner({ children, context, basePath, maxConcurrentUploads }) {
|
|
300
318
|
const apiPath = basePath ? `${basePath}` : '/api/edgestore';
|
|
319
|
+
const [state, setState] = React.useState({
|
|
320
|
+
loading: true,
|
|
321
|
+
initialized: false,
|
|
322
|
+
error: false
|
|
323
|
+
});
|
|
301
324
|
const uploadingCountRef = React.useRef(0);
|
|
325
|
+
const initExecuted = React.useRef(false); // to make sure we don't run init twice
|
|
302
326
|
React.useEffect(()=>{
|
|
303
|
-
|
|
327
|
+
if (!initExecuted.current) {
|
|
328
|
+
void init();
|
|
329
|
+
}
|
|
330
|
+
return ()=>{
|
|
331
|
+
initExecuted.current = true;
|
|
332
|
+
};
|
|
304
333
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
305
334
|
}, []);
|
|
306
335
|
async function init() {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
336
|
+
try {
|
|
337
|
+
setState({
|
|
338
|
+
loading: true,
|
|
339
|
+
initialized: false,
|
|
340
|
+
error: false
|
|
341
|
+
});
|
|
342
|
+
const res = await fetch(`${apiPath}/init`, {
|
|
343
|
+
method: 'POST'
|
|
344
|
+
});
|
|
345
|
+
if (res.ok) {
|
|
346
|
+
const json = await res.json();
|
|
347
|
+
const innerRes = await fetch(`${DEFAULT_BASE_URL}/_init`, {
|
|
348
|
+
method: 'GET',
|
|
349
|
+
credentials: 'include',
|
|
350
|
+
headers: {
|
|
351
|
+
'x-edgestore-token': json.token
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
if (innerRes.ok) {
|
|
355
|
+
// update state
|
|
356
|
+
setState({
|
|
357
|
+
loading: false,
|
|
358
|
+
initialized: true,
|
|
359
|
+
error: false
|
|
360
|
+
});
|
|
361
|
+
} else {
|
|
362
|
+
setState({
|
|
363
|
+
loading: false,
|
|
364
|
+
initialized: false,
|
|
365
|
+
error: true
|
|
366
|
+
});
|
|
367
|
+
throw new EdgeStoreClientError("Couldn't initialize Edge Store.");
|
|
316
368
|
}
|
|
369
|
+
} else {
|
|
370
|
+
setState({
|
|
371
|
+
loading: false,
|
|
372
|
+
initialized: false,
|
|
373
|
+
error: true
|
|
374
|
+
});
|
|
375
|
+
await handleError(res);
|
|
376
|
+
}
|
|
377
|
+
} catch (err) {
|
|
378
|
+
setState({
|
|
379
|
+
loading: false,
|
|
380
|
+
initialized: false,
|
|
381
|
+
error: true
|
|
317
382
|
});
|
|
383
|
+
throw err;
|
|
318
384
|
}
|
|
319
385
|
}
|
|
320
386
|
async function reset() {
|
|
@@ -327,7 +393,8 @@ function EdgeStoreProviderInner({ children, context, basePath, maxConcurrentUplo
|
|
|
327
393
|
uploadingCountRef,
|
|
328
394
|
maxConcurrentUploads
|
|
329
395
|
}),
|
|
330
|
-
reset
|
|
396
|
+
reset,
|
|
397
|
+
state
|
|
331
398
|
}
|
|
332
399
|
}, children));
|
|
333
400
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EdgeStoreClientError.d.ts","sourceRoot":"","sources":["../../../src/libs/errors/EdgeStoreClientError.ts"],"names":[],"mappings":"AAAA,cAAM,oBAAqB,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM;CAI5B;AAED,eAAe,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handleError.d.ts","sourceRoot":"","sources":["../../../src/libs/errors/handleError.ts"],"names":[],"mappings":"AAGA,wBAAsB,WAAW,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,CAS/D"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edgestore/react",
|
|
3
|
-
"version": "0.1.5-alpha.
|
|
3
|
+
"version": "0.1.5-alpha.12",
|
|
4
4
|
"description": "Upload files with ease from React/Next.js",
|
|
5
5
|
"homepage": "https://edgestore.dev",
|
|
6
6
|
"repository": "https://github.com/edgestorejs/edgestore.git",
|
|
@@ -60,14 +60,14 @@
|
|
|
60
60
|
"uuid": "^9.0.0"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
|
-
"@edgestore/server": "0.1.5-alpha.
|
|
63
|
+
"@edgestore/server": "0.1.5-alpha.12",
|
|
64
64
|
"next": "*",
|
|
65
65
|
"react": ">=16.8.0",
|
|
66
66
|
"react-dom": ">=16.8.0",
|
|
67
67
|
"zod": ">=3.0.0"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@edgestore/server": "0.1.5-alpha.
|
|
70
|
+
"@edgestore/server": "0.1.5-alpha.12",
|
|
71
71
|
"@types/cookie": "^0.5.1",
|
|
72
72
|
"@types/node": "^18.11.18",
|
|
73
73
|
"@types/uuid": "^9.0.1",
|
|
@@ -77,5 +77,5 @@
|
|
|
77
77
|
"typescript": "^5.1.6",
|
|
78
78
|
"zod": "^3.21.4"
|
|
79
79
|
},
|
|
80
|
-
"gitHead": "
|
|
80
|
+
"gitHead": "ddd35396955072690b24e55c37f6423445d26644"
|
|
81
81
|
}
|
package/src/contextProvider.tsx
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { type AnyRouter } from '@edgestore/server/core';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { createNextProxy, type BucketFunctions } from './createNextProxy';
|
|
4
|
+
import EdgeStoreClientError from './libs/errors/EdgeStoreClientError';
|
|
5
|
+
import { handleError } from './libs/errors/handleError';
|
|
4
6
|
|
|
5
7
|
const DEFAULT_BASE_URL =
|
|
6
|
-
process
|
|
8
|
+
(process !== undefined
|
|
9
|
+
? process.env.NEXT_PUBLIC_EDGE_STORE_BASE_URL
|
|
10
|
+
: // @ts-expect-error - In Vite, the env variables are available on `import.meta`.
|
|
11
|
+
import.meta.env?.EDGE_STORE_BASE_URL) ?? 'https://files.edgestore.dev';
|
|
7
12
|
|
|
8
13
|
type EdgeStoreContextValue<TRouter extends AnyRouter> = {
|
|
9
14
|
edgestore: BucketFunctions<TRouter>;
|
|
@@ -14,6 +19,13 @@ type EdgeStoreContextValue<TRouter extends AnyRouter> = {
|
|
|
14
19
|
* Can be used after a sign-in or sign-out, for example.
|
|
15
20
|
*/
|
|
16
21
|
reset: () => Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* The current state of the Edge Store provider.
|
|
24
|
+
*
|
|
25
|
+
* You can use this to wait for the provider to be initialized
|
|
26
|
+
* before trying to show private images on your app.
|
|
27
|
+
*/
|
|
28
|
+
state: ProviderState;
|
|
17
29
|
};
|
|
18
30
|
|
|
19
31
|
export function createEdgeStoreProvider<TRouter extends AnyRouter>(opts?: {
|
|
@@ -75,6 +87,23 @@ export function createEdgeStoreProvider<TRouter extends AnyRouter>(opts?: {
|
|
|
75
87
|
};
|
|
76
88
|
}
|
|
77
89
|
|
|
90
|
+
type ProviderState =
|
|
91
|
+
| {
|
|
92
|
+
loading: true;
|
|
93
|
+
initialized: false;
|
|
94
|
+
error: false;
|
|
95
|
+
}
|
|
96
|
+
| {
|
|
97
|
+
loading: false;
|
|
98
|
+
initialized: false;
|
|
99
|
+
error: true;
|
|
100
|
+
}
|
|
101
|
+
| {
|
|
102
|
+
loading: false;
|
|
103
|
+
initialized: true;
|
|
104
|
+
error: false;
|
|
105
|
+
};
|
|
106
|
+
|
|
78
107
|
function EdgeStoreProviderInner<TRouter extends AnyRouter>({
|
|
79
108
|
children,
|
|
80
109
|
context,
|
|
@@ -87,24 +116,73 @@ function EdgeStoreProviderInner<TRouter extends AnyRouter>({
|
|
|
87
116
|
maxConcurrentUploads?: number;
|
|
88
117
|
}) {
|
|
89
118
|
const apiPath = basePath ? `${basePath}` : '/api/edgestore';
|
|
119
|
+
const [state, setState] = React.useState<ProviderState>({
|
|
120
|
+
loading: true,
|
|
121
|
+
initialized: false,
|
|
122
|
+
error: false,
|
|
123
|
+
});
|
|
90
124
|
const uploadingCountRef = React.useRef(0);
|
|
125
|
+
const initExecuted = React.useRef(false); // to make sure we don't run init twice
|
|
91
126
|
React.useEffect(() => {
|
|
92
|
-
|
|
127
|
+
if (!initExecuted.current) {
|
|
128
|
+
void init();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return () => {
|
|
132
|
+
initExecuted.current = true;
|
|
133
|
+
};
|
|
93
134
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
94
135
|
}, []);
|
|
95
136
|
|
|
96
137
|
async function init() {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
138
|
+
try {
|
|
139
|
+
setState({
|
|
140
|
+
loading: true,
|
|
141
|
+
initialized: false,
|
|
142
|
+
error: false,
|
|
143
|
+
});
|
|
144
|
+
const res = await fetch(`${apiPath}/init`, {
|
|
145
|
+
method: 'POST',
|
|
146
|
+
});
|
|
147
|
+
if (res.ok) {
|
|
148
|
+
const json = await res.json();
|
|
149
|
+
const innerRes = await fetch(`${DEFAULT_BASE_URL}/_init`, {
|
|
150
|
+
method: 'GET',
|
|
151
|
+
credentials: 'include',
|
|
152
|
+
headers: {
|
|
153
|
+
'x-edgestore-token': json.token,
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
if (innerRes.ok) {
|
|
157
|
+
// update state
|
|
158
|
+
setState({
|
|
159
|
+
loading: false,
|
|
160
|
+
initialized: true,
|
|
161
|
+
error: false,
|
|
162
|
+
});
|
|
163
|
+
} else {
|
|
164
|
+
setState({
|
|
165
|
+
loading: false,
|
|
166
|
+
initialized: false,
|
|
167
|
+
error: true,
|
|
168
|
+
});
|
|
169
|
+
throw new EdgeStoreClientError("Couldn't initialize Edge Store.");
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
setState({
|
|
173
|
+
loading: false,
|
|
174
|
+
initialized: false,
|
|
175
|
+
error: true,
|
|
176
|
+
});
|
|
177
|
+
await handleError(res);
|
|
178
|
+
}
|
|
179
|
+
} catch (err) {
|
|
180
|
+
setState({
|
|
181
|
+
loading: false,
|
|
182
|
+
initialized: false,
|
|
183
|
+
error: true,
|
|
107
184
|
});
|
|
185
|
+
throw err;
|
|
108
186
|
}
|
|
109
187
|
}
|
|
110
188
|
|
|
@@ -122,6 +200,7 @@ function EdgeStoreProviderInner<TRouter extends AnyRouter>({
|
|
|
122
200
|
maxConcurrentUploads,
|
|
123
201
|
}),
|
|
124
202
|
reset,
|
|
203
|
+
state,
|
|
125
204
|
}}
|
|
126
205
|
>
|
|
127
206
|
{children}
|
package/src/createNextProxy.ts
CHANGED
|
@@ -3,9 +3,11 @@ import {
|
|
|
3
3
|
type AnyRouter,
|
|
4
4
|
type InferBucketPathObject,
|
|
5
5
|
type InferMetadataObject,
|
|
6
|
+
type UploadOptions,
|
|
6
7
|
} from '@edgestore/server/core';
|
|
7
8
|
import { type z } from 'zod';
|
|
8
|
-
import
|
|
9
|
+
import EdgeStoreClientError from './libs/errors/EdgeStoreClientError';
|
|
10
|
+
import { handleError } from './libs/errors/handleError';
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* @internal
|
|
@@ -62,36 +64,6 @@ export type BucketFunctions<TRouter extends AnyRouter> = {
|
|
|
62
64
|
|
|
63
65
|
type OnProgressChangeHandler = (progress: number) => void;
|
|
64
66
|
|
|
65
|
-
type UploadOptions = {
|
|
66
|
-
/**
|
|
67
|
-
* e.g. 'my-file-name.jpg'
|
|
68
|
-
*
|
|
69
|
-
* By default, a unique file name will be generated for each upload.
|
|
70
|
-
* If you want to use a custom file name, you can use this option.
|
|
71
|
-
* If you use the same file name for multiple uploads, the previous file will be overwritten.
|
|
72
|
-
* But it might take some time for the CDN cache to be cleared.
|
|
73
|
-
* So maybe you will keep seeing the old file for a while.
|
|
74
|
-
*
|
|
75
|
-
* If you want to replace an existing file immediately leave the `manualFileName` option empty and use the `replaceTargetUrl` option.
|
|
76
|
-
*/
|
|
77
|
-
manualFileName?: string;
|
|
78
|
-
/**
|
|
79
|
-
* Use this to replace an existing file.
|
|
80
|
-
* It will automatically delete the existing file when the upload is complete.
|
|
81
|
-
*/
|
|
82
|
-
replaceTargetUrl?: string;
|
|
83
|
-
/**
|
|
84
|
-
* If true, the file needs to be confirmed by using the `confirmUpload` function.
|
|
85
|
-
* If the file is not confirmed within 24 hours, it will be deleted.
|
|
86
|
-
*
|
|
87
|
-
* This is useful for pages where the file is uploaded as soon as it is selected,
|
|
88
|
-
* but the user can leave the page without submitting the form.
|
|
89
|
-
*
|
|
90
|
-
* This avoids unnecessary zombie files in the bucket.
|
|
91
|
-
*/
|
|
92
|
-
temporary?: boolean;
|
|
93
|
-
};
|
|
94
|
-
|
|
95
67
|
export function createNextProxy<TRouter extends AnyRouter>({
|
|
96
68
|
apiPath,
|
|
97
69
|
uploadingCountRef,
|
|
@@ -129,7 +101,7 @@ export function createNextProxy<TRouter extends AnyRouter>({
|
|
|
129
101
|
apiPath,
|
|
130
102
|
});
|
|
131
103
|
if (!success) {
|
|
132
|
-
throw new
|
|
104
|
+
throw new EdgeStoreClientError('Failed to confirm upload');
|
|
133
105
|
}
|
|
134
106
|
},
|
|
135
107
|
delete: async (params: { url: string }) => {
|
|
@@ -138,7 +110,7 @@ export function createNextProxy<TRouter extends AnyRouter>({
|
|
|
138
110
|
apiPath,
|
|
139
111
|
});
|
|
140
112
|
if (!success) {
|
|
141
|
-
throw new
|
|
113
|
+
throw new EdgeStoreClientError('Failed to delete file');
|
|
142
114
|
}
|
|
143
115
|
},
|
|
144
116
|
};
|
|
@@ -187,6 +159,9 @@ async function uploadFile(
|
|
|
187
159
|
'Content-Type': 'application/json',
|
|
188
160
|
},
|
|
189
161
|
});
|
|
162
|
+
if (!res.ok) {
|
|
163
|
+
await handleError(res);
|
|
164
|
+
}
|
|
190
165
|
const json = (await res.json()) as RequestUploadRes;
|
|
191
166
|
if ('multipart' in json) {
|
|
192
167
|
await multipartUpload({
|
|
@@ -201,7 +176,7 @@ async function uploadFile(
|
|
|
201
176
|
// Upload the file to the signed URL and get the progress
|
|
202
177
|
await uploadFileInner(file, json.uploadUrl, onProgressChange);
|
|
203
178
|
} else {
|
|
204
|
-
throw new
|
|
179
|
+
throw new EdgeStoreClientError('An error occurred');
|
|
205
180
|
}
|
|
206
181
|
return {
|
|
207
182
|
url: getUrl(json.accessUrl, apiPath),
|
|
@@ -226,7 +201,14 @@ async function uploadFile(
|
|
|
226
201
|
* we need to proxy the file through the server.
|
|
227
202
|
*/
|
|
228
203
|
function getUrl(url: string, apiPath: string) {
|
|
229
|
-
|
|
204
|
+
const mode =
|
|
205
|
+
process !== undefined
|
|
206
|
+
? process.env.NODE_ENV
|
|
207
|
+
: // @ts-expect-error - DEV is injected by Vite
|
|
208
|
+
import.meta.env?.DEV
|
|
209
|
+
? 'development'
|
|
210
|
+
: 'production';
|
|
211
|
+
if (mode === 'development' && !url.includes('/_public/')) {
|
|
230
212
|
const proxyUrl = new URL(window.location.origin);
|
|
231
213
|
proxyUrl.pathname = `${apiPath}/proxy-file`;
|
|
232
214
|
proxyUrl.search = new URLSearchParams({
|
|
@@ -245,6 +227,8 @@ const uploadFileInner = async (
|
|
|
245
227
|
const promise = new Promise<string | null>((resolve, reject) => {
|
|
246
228
|
const request = new XMLHttpRequest();
|
|
247
229
|
request.open('PUT', uploadUrl);
|
|
230
|
+
// This is for Azure provider. Specifies the blob type
|
|
231
|
+
request.setRequestHeader('x-ms-blob-type', 'BlockBlob');
|
|
248
232
|
request.addEventListener('loadstart', () => {
|
|
249
233
|
onProgressChange?.(0);
|
|
250
234
|
});
|
|
@@ -310,7 +294,9 @@ async function multipartUpload(params: {
|
|
|
310
294
|
onProgressChange?.(totalProgress);
|
|
311
295
|
});
|
|
312
296
|
if (!eTag) {
|
|
313
|
-
throw new
|
|
297
|
+
throw new EdgeStoreClientError(
|
|
298
|
+
'Could not get ETag from multipart response',
|
|
299
|
+
);
|
|
314
300
|
}
|
|
315
301
|
return {
|
|
316
302
|
partNumber: part.partNumber,
|
|
@@ -346,7 +332,7 @@ async function multipartUpload(params: {
|
|
|
346
332
|
},
|
|
347
333
|
});
|
|
348
334
|
if (!res.ok) {
|
|
349
|
-
|
|
335
|
+
await handleError(res);
|
|
350
336
|
}
|
|
351
337
|
}
|
|
352
338
|
|
|
@@ -375,7 +361,7 @@ async function confirmUpload(
|
|
|
375
361
|
},
|
|
376
362
|
});
|
|
377
363
|
if (!res.ok) {
|
|
378
|
-
|
|
364
|
+
await handleError(res);
|
|
379
365
|
}
|
|
380
366
|
return res.json();
|
|
381
367
|
}
|
|
@@ -405,7 +391,7 @@ async function deleteFile(
|
|
|
405
391
|
},
|
|
406
392
|
});
|
|
407
393
|
if (!res.ok) {
|
|
408
|
-
|
|
394
|
+
await handleError(res);
|
|
409
395
|
}
|
|
410
396
|
return res.json();
|
|
411
397
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { EdgeStoreApiClientError } from '@edgestore/server/core';
|
|
2
|
+
import EdgeStoreClientError from './EdgeStoreClientError';
|
|
3
|
+
|
|
4
|
+
export async function handleError(res: Response): Promise<never> {
|
|
5
|
+
try {
|
|
6
|
+
const json = await res.json();
|
|
7
|
+
throw new EdgeStoreApiClientError({ response: json });
|
|
8
|
+
} catch (err) {
|
|
9
|
+
throw new EdgeStoreClientError(
|
|
10
|
+
`Failed to parse response. Make sure the api is correctly configured at ${res.url}`,
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"EdgeStoreError.d.ts","sourceRoot":"","sources":["../../../src/libs/errors/EdgeStoreError.ts"],"names":[],"mappings":"AAAA,cAAM,cAAe,SAAQ,KAAK;gBACpB,OAAO,EAAE,MAAM;CAI5B;AAED,eAAe,cAAc,CAAC"}
|