@edgestore/react 0.1.5-alpha.1 → 0.1.5-alpha.10

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.
@@ -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;AAK1E,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;CAC5B,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"}
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;AAO1E,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,EACzB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,CAAC;AAG7B;;;GAGG;AACH,MAAM,MAAM,QAAQ,CAAC,KAAK,IAAI;KAC3B,CAAC,IAAI,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC;CAE7B,GAAG,EAAE,CAAC;AAEP,MAAM,MAAM,eAAe,CAAC,OAAO,SAAS,SAAS,IAAI;KACtD,CAAC,IAAI,MAAM,OAAO,CAAC,SAAS,CAAC,GAAG;QAC/B,MAAM,EAAE,CACN,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS,KAAK,GACjE;YACE,IAAI,EAAE,IAAI,CAAC;YACX,gBAAgB,CAAC,EAAE,uBAAuB,CAAC;YAC3C,OAAO,CAAC,EAAE,aAAa,CAAC;SACzB,GACD;YACE,IAAI,EAAE,IAAI,CAAC;YACX,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;YACvD,gBAAgB,CAAC,EAAE,uBAAuB,CAAC;YAC3C,OAAO,CAAC,EAAE,aAAa,CAAC;SACzB,KACF,OAAO,CACV,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,SAAS,OAAO,GACjD;YACE,GAAG,EAAE,MAAM,CAAC;YACZ,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;YAC5B,IAAI,EAAE,MAAM,CAAC;YACb,UAAU,EAAE,IAAI,CAAC;YACjB,QAAQ,EAAE,mBAAmB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACrD,IAAI,EAAE,qBAAqB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACnD,SAAS,EAAE,QAAQ,CACjB,MAAM,qBAAqB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CACnD,EAAE,CAAC;SACL,GACD;YACE,GAAG,EAAE,MAAM,CAAC;YACZ,IAAI,EAAE,MAAM,CAAC;YACb,UAAU,EAAE,IAAI,CAAC;YACjB,QAAQ,EAAE,mBAAmB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACrD,IAAI,EAAE,qBAAqB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACnD,SAAS,EAAE,QAAQ,CACjB,MAAM,qBAAqB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CACnD,EAAE,CAAC;SACL,CACN,CAAC;QACF,aAAa,EAAE,CAAC,MAAM,EAAE;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;QAC1D,MAAM,EAAE,CAAC,MAAM,EAAE;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KACpD;CACF,CAAC;AAEF,KAAK,uBAAuB,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;AAE1D,KAAK,aAAa,GAAG;IACnB;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wBAAgB,eAAe,CAAC,OAAO,SAAS,SAAS,EAAE,EACzD,OAAO,EACP,iBAAiB,EACjB,oBAAwB,GACzB,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAClD,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,4BA6CA"}
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 EdgeStoreError extends Error {
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 EdgeStoreError('Failed to confirm upload');
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 EdgeStoreError('Failed to delete file');
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 EdgeStoreError('An error occurred');
127
+ throw new EdgeStoreClientError('An error occurred');
113
128
  }
114
129
  return {
115
130
  url: getUrl(json.accessUrl, apiPath),
@@ -144,6 +159,8 @@ const uploadFileInner = async (file, uploadUrl, onProgressChange)=>{
144
159
  const promise = new Promise((resolve, reject)=>{
145
160
  const request = new XMLHttpRequest();
146
161
  request.open('PUT', uploadUrl);
162
+ // This is for Azure provider. Specifies the blob type
163
+ request.setRequestHeader('x-ms-blob-type', 'BlockBlob');
147
164
  request.addEventListener('loadstart', ()=>{
148
165
  onProgressChange?.(0);
149
166
  });
@@ -189,7 +206,7 @@ async function multipartUpload(params) {
189
206
  onProgressChange?.(totalProgress);
190
207
  });
191
208
  if (!eTag) {
192
- throw new EdgeStoreError('Could not get ETag from multipart response');
209
+ throw new EdgeStoreClientError('Could not get ETag from multipart response');
193
210
  }
194
211
  return {
195
212
  partNumber: part.partNumber,
@@ -220,7 +237,7 @@ async function multipartUpload(params) {
220
237
  }
221
238
  });
222
239
  if (!res.ok) {
223
- throw new EdgeStoreError('Multi-part upload failed');
240
+ await handleError(res);
224
241
  }
225
242
  }
226
243
  async function confirmUpload({ url }, { apiPath, bucketName }) {
@@ -235,7 +252,7 @@ async function confirmUpload({ url }, { apiPath, bucketName }) {
235
252
  }
236
253
  });
237
254
  if (!res.ok) {
238
- throw new EdgeStoreError('An error occurred');
255
+ await handleError(res);
239
256
  }
240
257
  return res.json();
241
258
  }
@@ -251,7 +268,7 @@ async function deleteFile({ url }, { apiPath, bucketName }) {
251
268
  }
252
269
  });
253
270
  if (!res.ok) {
254
- throw new EdgeStoreError('An error occurred');
271
+ await handleError(res);
255
272
  }
256
273
  return res.json();
257
274
  }
@@ -322,23 +339,71 @@ function createEdgeStoreProvider(opts) {
322
339
  }
323
340
  function EdgeStoreProviderInner({ children, context, basePath, maxConcurrentUploads }) {
324
341
  const apiPath = basePath ? `${basePath}` : '/api/edgestore';
342
+ const [state, setState] = React__namespace.useState({
343
+ loading: true,
344
+ initialized: false,
345
+ error: false
346
+ });
325
347
  const uploadingCountRef = React__namespace.useRef(0);
348
+ const initExecuted = React__namespace.useRef(false); // to make sure we don't run init twice
326
349
  React__namespace.useEffect(()=>{
327
- void init();
350
+ if (!initExecuted.current) {
351
+ void init();
352
+ }
353
+ return ()=>{
354
+ initExecuted.current = true;
355
+ };
328
356
  // eslint-disable-next-line react-hooks/exhaustive-deps
329
357
  }, []);
330
358
  async function init() {
331
- const res = await fetch(`${apiPath}/init`, {
332
- method: 'POST'
333
- });
334
- if (res.ok) {
335
- const json = await res.json();
336
- await fetch(`${DEFAULT_BASE_URL}/_init`, {
337
- method: 'GET',
338
- headers: {
339
- 'x-edgestore-token': json.token
359
+ try {
360
+ setState({
361
+ loading: true,
362
+ initialized: false,
363
+ error: false
364
+ });
365
+ const res = await fetch(`${apiPath}/init`, {
366
+ method: 'POST'
367
+ });
368
+ if (res.ok) {
369
+ const json = await res.json();
370
+ const innerRes = await fetch(`${DEFAULT_BASE_URL}/_init`, {
371
+ method: 'GET',
372
+ credentials: 'include',
373
+ headers: {
374
+ 'x-edgestore-token': json.token
375
+ }
376
+ });
377
+ if (innerRes.ok) {
378
+ // update state
379
+ setState({
380
+ loading: false,
381
+ initialized: true,
382
+ error: false
383
+ });
384
+ } else {
385
+ setState({
386
+ loading: false,
387
+ initialized: false,
388
+ error: true
389
+ });
390
+ throw new EdgeStoreClientError("Couldn't initialize Edge Store.");
340
391
  }
392
+ } else {
393
+ setState({
394
+ loading: false,
395
+ initialized: false,
396
+ error: true
397
+ });
398
+ await handleError(res);
399
+ }
400
+ } catch (err) {
401
+ setState({
402
+ loading: false,
403
+ initialized: false,
404
+ error: true
341
405
  });
406
+ throw err;
342
407
  }
343
408
  }
344
409
  async function reset() {
@@ -351,7 +416,8 @@ function EdgeStoreProviderInner({ children, context, basePath, maxConcurrentUplo
351
416
  uploadingCountRef,
352
417
  maxConcurrentUploads
353
418
  }),
354
- reset
419
+ reset,
420
+ state
355
421
  }
356
422
  }, children));
357
423
  }
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 EdgeStoreError extends Error {
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 EdgeStoreError('Failed to confirm upload');
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 EdgeStoreError('Failed to delete file');
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 EdgeStoreError('An error occurred');
103
+ throw new EdgeStoreClientError('An error occurred');
89
104
  }
90
105
  return {
91
106
  url: getUrl(json.accessUrl, apiPath),
@@ -120,6 +135,8 @@ const uploadFileInner = async (file, uploadUrl, onProgressChange)=>{
120
135
  const promise = new Promise((resolve, reject)=>{
121
136
  const request = new XMLHttpRequest();
122
137
  request.open('PUT', uploadUrl);
138
+ // This is for Azure provider. Specifies the blob type
139
+ request.setRequestHeader('x-ms-blob-type', 'BlockBlob');
123
140
  request.addEventListener('loadstart', ()=>{
124
141
  onProgressChange?.(0);
125
142
  });
@@ -165,7 +182,7 @@ async function multipartUpload(params) {
165
182
  onProgressChange?.(totalProgress);
166
183
  });
167
184
  if (!eTag) {
168
- throw new EdgeStoreError('Could not get ETag from multipart response');
185
+ throw new EdgeStoreClientError('Could not get ETag from multipart response');
169
186
  }
170
187
  return {
171
188
  partNumber: part.partNumber,
@@ -196,7 +213,7 @@ async function multipartUpload(params) {
196
213
  }
197
214
  });
198
215
  if (!res.ok) {
199
- throw new EdgeStoreError('Multi-part upload failed');
216
+ await handleError(res);
200
217
  }
201
218
  }
202
219
  async function confirmUpload({ url }, { apiPath, bucketName }) {
@@ -211,7 +228,7 @@ async function confirmUpload({ url }, { apiPath, bucketName }) {
211
228
  }
212
229
  });
213
230
  if (!res.ok) {
214
- throw new EdgeStoreError('An error occurred');
231
+ await handleError(res);
215
232
  }
216
233
  return res.json();
217
234
  }
@@ -227,7 +244,7 @@ async function deleteFile({ url }, { apiPath, bucketName }) {
227
244
  }
228
245
  });
229
246
  if (!res.ok) {
230
- throw new EdgeStoreError('An error occurred');
247
+ await handleError(res);
231
248
  }
232
249
  return res.json();
233
250
  }
@@ -298,23 +315,71 @@ function createEdgeStoreProvider(opts) {
298
315
  }
299
316
  function EdgeStoreProviderInner({ children, context, basePath, maxConcurrentUploads }) {
300
317
  const apiPath = basePath ? `${basePath}` : '/api/edgestore';
318
+ const [state, setState] = React.useState({
319
+ loading: true,
320
+ initialized: false,
321
+ error: false
322
+ });
301
323
  const uploadingCountRef = React.useRef(0);
324
+ const initExecuted = React.useRef(false); // to make sure we don't run init twice
302
325
  React.useEffect(()=>{
303
- void init();
326
+ if (!initExecuted.current) {
327
+ void init();
328
+ }
329
+ return ()=>{
330
+ initExecuted.current = true;
331
+ };
304
332
  // eslint-disable-next-line react-hooks/exhaustive-deps
305
333
  }, []);
306
334
  async function init() {
307
- const res = await fetch(`${apiPath}/init`, {
308
- method: 'POST'
309
- });
310
- if (res.ok) {
311
- const json = await res.json();
312
- await fetch(`${DEFAULT_BASE_URL}/_init`, {
313
- method: 'GET',
314
- headers: {
315
- 'x-edgestore-token': json.token
335
+ try {
336
+ setState({
337
+ loading: true,
338
+ initialized: false,
339
+ error: false
340
+ });
341
+ const res = await fetch(`${apiPath}/init`, {
342
+ method: 'POST'
343
+ });
344
+ if (res.ok) {
345
+ const json = await res.json();
346
+ const innerRes = await fetch(`${DEFAULT_BASE_URL}/_init`, {
347
+ method: 'GET',
348
+ credentials: 'include',
349
+ headers: {
350
+ 'x-edgestore-token': json.token
351
+ }
352
+ });
353
+ if (innerRes.ok) {
354
+ // update state
355
+ setState({
356
+ loading: false,
357
+ initialized: true,
358
+ error: false
359
+ });
360
+ } else {
361
+ setState({
362
+ loading: false,
363
+ initialized: false,
364
+ error: true
365
+ });
366
+ throw new EdgeStoreClientError("Couldn't initialize Edge Store.");
316
367
  }
368
+ } else {
369
+ setState({
370
+ loading: false,
371
+ initialized: false,
372
+ error: true
373
+ });
374
+ await handleError(res);
375
+ }
376
+ } catch (err) {
377
+ setState({
378
+ loading: false,
379
+ initialized: false,
380
+ error: true
317
381
  });
382
+ throw err;
318
383
  }
319
384
  }
320
385
  async function reset() {
@@ -327,7 +392,8 @@ function EdgeStoreProviderInner({ children, context, basePath, maxConcurrentUplo
327
392
  uploadingCountRef,
328
393
  maxConcurrentUploads
329
394
  }),
330
- reset
395
+ reset,
396
+ state
331
397
  }
332
398
  }, children));
333
399
  }
@@ -0,0 +1,5 @@
1
+ declare class EdgeStoreClientError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export default EdgeStoreClientError;
5
+ //# sourceMappingURL=EdgeStoreClientError.d.ts.map
@@ -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,2 @@
1
+ export declare function handleError(res: Response): Promise<never>;
2
+ //# sourceMappingURL=handleError.d.ts.map
@@ -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.1",
3
+ "version": "0.1.5-alpha.10",
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.1",
63
+ "@edgestore/server": "0.1.5-alpha.10",
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.1",
70
+ "@edgestore/server": "0.1.5-alpha.10",
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": "94b31fd879a0f4f9ed13cc08c9646e29d03c3f92"
80
+ "gitHead": "6cf9f5bbea5c1cf990b8b8caa70bc4341e046e59"
81
81
  }
@@ -1,6 +1,8 @@
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
8
  process.env.NEXT_PUBLIC_EDGE_STORE_BASE_URL ?? 'https://files.edgestore.dev';
@@ -14,6 +16,13 @@ type EdgeStoreContextValue<TRouter extends AnyRouter> = {
14
16
  * Can be used after a sign-in or sign-out, for example.
15
17
  */
16
18
  reset: () => Promise<void>;
19
+ /**
20
+ * The current state of the Edge Store provider.
21
+ *
22
+ * You can use this to wait for the provider to be initialized
23
+ * before trying to show private images on your app.
24
+ */
25
+ state: ProviderState;
17
26
  };
18
27
 
19
28
  export function createEdgeStoreProvider<TRouter extends AnyRouter>(opts?: {
@@ -75,6 +84,23 @@ export function createEdgeStoreProvider<TRouter extends AnyRouter>(opts?: {
75
84
  };
76
85
  }
77
86
 
87
+ type ProviderState =
88
+ | {
89
+ loading: true;
90
+ initialized: false;
91
+ error: false;
92
+ }
93
+ | {
94
+ loading: false;
95
+ initialized: false;
96
+ error: true;
97
+ }
98
+ | {
99
+ loading: false;
100
+ initialized: true;
101
+ error: false;
102
+ };
103
+
78
104
  function EdgeStoreProviderInner<TRouter extends AnyRouter>({
79
105
  children,
80
106
  context,
@@ -87,24 +113,73 @@ function EdgeStoreProviderInner<TRouter extends AnyRouter>({
87
113
  maxConcurrentUploads?: number;
88
114
  }) {
89
115
  const apiPath = basePath ? `${basePath}` : '/api/edgestore';
116
+ const [state, setState] = React.useState<ProviderState>({
117
+ loading: true,
118
+ initialized: false,
119
+ error: false,
120
+ });
90
121
  const uploadingCountRef = React.useRef(0);
122
+ const initExecuted = React.useRef(false); // to make sure we don't run init twice
91
123
  React.useEffect(() => {
92
- void init();
124
+ if (!initExecuted.current) {
125
+ void init();
126
+ }
127
+
128
+ return () => {
129
+ initExecuted.current = true;
130
+ };
93
131
  // eslint-disable-next-line react-hooks/exhaustive-deps
94
132
  }, []);
95
133
 
96
134
  async function init() {
97
- const res = await fetch(`${apiPath}/init`, {
98
- method: 'POST',
99
- });
100
- if (res.ok) {
101
- const json = await res.json();
102
- await fetch(`${DEFAULT_BASE_URL}/_init`, {
103
- method: 'GET',
104
- headers: {
105
- 'x-edgestore-token': json.token,
106
- },
135
+ try {
136
+ setState({
137
+ loading: true,
138
+ initialized: false,
139
+ error: false,
140
+ });
141
+ const res = await fetch(`${apiPath}/init`, {
142
+ method: 'POST',
143
+ });
144
+ if (res.ok) {
145
+ const json = await res.json();
146
+ const innerRes = await fetch(`${DEFAULT_BASE_URL}/_init`, {
147
+ method: 'GET',
148
+ credentials: 'include',
149
+ headers: {
150
+ 'x-edgestore-token': json.token,
151
+ },
152
+ });
153
+ if (innerRes.ok) {
154
+ // update state
155
+ setState({
156
+ loading: false,
157
+ initialized: true,
158
+ error: false,
159
+ });
160
+ } else {
161
+ setState({
162
+ loading: false,
163
+ initialized: false,
164
+ error: true,
165
+ });
166
+ throw new EdgeStoreClientError("Couldn't initialize Edge Store.");
167
+ }
168
+ } else {
169
+ setState({
170
+ loading: false,
171
+ initialized: false,
172
+ error: true,
173
+ });
174
+ await handleError(res);
175
+ }
176
+ } catch (err) {
177
+ setState({
178
+ loading: false,
179
+ initialized: false,
180
+ error: true,
107
181
  });
182
+ throw err;
108
183
  }
109
184
  }
110
185
 
@@ -122,6 +197,7 @@ function EdgeStoreProviderInner<TRouter extends AnyRouter>({
122
197
  maxConcurrentUploads,
123
198
  }),
124
199
  reset,
200
+ state,
125
201
  }}
126
202
  >
127
203
  {children}
@@ -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 EdgeStoreError from './libs/errors/EdgeStoreError';
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 EdgeStoreError('Failed to confirm upload');
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 EdgeStoreError('Failed to delete file');
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 EdgeStoreError('An error occurred');
179
+ throw new EdgeStoreClientError('An error occurred');
205
180
  }
206
181
  return {
207
182
  url: getUrl(json.accessUrl, apiPath),
@@ -245,6 +220,8 @@ const uploadFileInner = async (
245
220
  const promise = new Promise<string | null>((resolve, reject) => {
246
221
  const request = new XMLHttpRequest();
247
222
  request.open('PUT', uploadUrl);
223
+ // This is for Azure provider. Specifies the blob type
224
+ request.setRequestHeader('x-ms-blob-type', 'BlockBlob');
248
225
  request.addEventListener('loadstart', () => {
249
226
  onProgressChange?.(0);
250
227
  });
@@ -310,7 +287,9 @@ async function multipartUpload(params: {
310
287
  onProgressChange?.(totalProgress);
311
288
  });
312
289
  if (!eTag) {
313
- throw new EdgeStoreError('Could not get ETag from multipart response');
290
+ throw new EdgeStoreClientError(
291
+ 'Could not get ETag from multipart response',
292
+ );
314
293
  }
315
294
  return {
316
295
  partNumber: part.partNumber,
@@ -346,7 +325,7 @@ async function multipartUpload(params: {
346
325
  },
347
326
  });
348
327
  if (!res.ok) {
349
- throw new EdgeStoreError('Multi-part upload failed');
328
+ await handleError(res);
350
329
  }
351
330
  }
352
331
 
@@ -375,7 +354,7 @@ async function confirmUpload(
375
354
  },
376
355
  });
377
356
  if (!res.ok) {
378
- throw new EdgeStoreError('An error occurred');
357
+ await handleError(res);
379
358
  }
380
359
  return res.json();
381
360
  }
@@ -405,7 +384,7 @@ async function deleteFile(
405
384
  },
406
385
  });
407
386
  if (!res.ok) {
408
- throw new EdgeStoreError('An error occurred');
387
+ await handleError(res);
409
388
  }
410
389
  return res.json();
411
390
  }
@@ -1,8 +1,8 @@
1
- class EdgeStoreError extends Error {
1
+ class EdgeStoreClientError extends Error {
2
2
  constructor(message: string) {
3
3
  super(message);
4
4
  this.name = 'EdgeStoreError';
5
5
  }
6
6
  }
7
7
 
8
- export default EdgeStoreError;
8
+ export default EdgeStoreClientError;
@@ -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,5 +0,0 @@
1
- declare class EdgeStoreError extends Error {
2
- constructor(message: string);
3
- }
4
- export default EdgeStoreError;
5
- //# sourceMappingURL=EdgeStoreError.d.ts.map
@@ -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"}