@edgestore/server 0.1.5-alpha.0 → 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.
Files changed (71) hide show
  1. package/adapters/express/index.d.ts +1 -0
  2. package/adapters/express/index.js +1 -0
  3. package/dist/adapters/express/index.d.ts +20 -0
  4. package/dist/adapters/express/index.d.ts.map +1 -0
  5. package/dist/adapters/express/index.js +117 -0
  6. package/dist/adapters/express/index.mjs +113 -0
  7. package/dist/adapters/next/app/index.d.ts +5 -3
  8. package/dist/adapters/next/app/index.d.ts.map +1 -1
  9. package/dist/adapters/next/app/index.js +45 -16
  10. package/dist/adapters/next/app/index.mjs +44 -15
  11. package/dist/adapters/next/pages/index.d.ts +5 -3
  12. package/dist/adapters/next/pages/index.d.ts.map +1 -1
  13. package/dist/adapters/next/pages/index.js +37 -13
  14. package/dist/adapters/next/pages/index.mjs +36 -12
  15. package/dist/adapters/shared.d.ts +19 -1
  16. package/dist/adapters/shared.d.ts.map +1 -1
  17. package/dist/core/client/index.d.ts +116 -4
  18. package/dist/core/client/index.d.ts.map +1 -1
  19. package/dist/core/index.d.ts +2 -0
  20. package/dist/core/index.d.ts.map +1 -1
  21. package/dist/core/index.js +109 -34
  22. package/dist/core/index.mjs +110 -36
  23. package/dist/core/sdk/index.d.ts.map +1 -1
  24. package/dist/{index-f33a00fb.js → index-0c401ce1.js} +38 -4
  25. package/dist/{index-50ab9e08.js → index-23d1ede9.mjs} +42 -7
  26. package/dist/{index-30a3741e.mjs → index-a2e7ca9e.js} +49 -4
  27. package/dist/libs/errors/EdgeStoreApiClientError.d.ts +8 -0
  28. package/dist/libs/errors/EdgeStoreApiClientError.d.ts.map +1 -0
  29. package/dist/libs/errors/EdgeStoreCredentialsError.d.ts.map +1 -1
  30. package/dist/libs/errors/EdgeStoreError.d.ts +36 -4
  31. package/dist/libs/errors/EdgeStoreError.d.ts.map +1 -1
  32. package/dist/libs/logger.d.ts +13 -0
  33. package/dist/libs/logger.d.ts.map +1 -0
  34. package/dist/logger-7ea2248c.mjs +40 -0
  35. package/dist/logger-b530a3e1.js +42 -0
  36. package/dist/logger-e0066db9.js +33 -0
  37. package/dist/providers/aws/index.js +1 -1
  38. package/dist/providers/aws/index.mjs +1 -1
  39. package/dist/providers/azure/index.d.ts +20 -0
  40. package/dist/providers/azure/index.d.ts.map +1 -0
  41. package/dist/providers/azure/index.js +61 -0
  42. package/dist/providers/azure/index.mjs +57 -0
  43. package/dist/providers/edgestore/index.d.ts.map +1 -1
  44. package/dist/providers/edgestore/index.js +10 -3
  45. package/dist/providers/edgestore/index.mjs +10 -3
  46. package/dist/providers/types.d.ts +1 -0
  47. package/dist/providers/types.d.ts.map +1 -1
  48. package/dist/{shared-306c1af2.js → shared-06cb0d86.js} +150 -61
  49. package/dist/{shared-9fad0d51.js → shared-d474acc6.js} +114 -46
  50. package/dist/{shared-6dea9e91.mjs → shared-d7ea66fb.mjs} +141 -52
  51. package/dist/types.d.ts +6 -0
  52. package/dist/types.d.ts.map +1 -1
  53. package/package.json +17 -2
  54. package/providers/azure/index.d.ts +1 -0
  55. package/providers/azure/index.js +1 -0
  56. package/src/adapters/express/index.ts +164 -0
  57. package/src/adapters/next/app/index.ts +63 -20
  58. package/src/adapters/next/pages/index.ts +52 -15
  59. package/src/adapters/shared.ts +142 -40
  60. package/src/core/client/index.ts +233 -51
  61. package/src/core/index.ts +6 -0
  62. package/src/core/sdk/index.ts +7 -1
  63. package/src/libs/errors/EdgeStoreApiClientError.ts +14 -0
  64. package/src/libs/errors/EdgeStoreCredentialsError.ts +1 -2
  65. package/src/libs/errors/EdgeStoreError.ts +74 -7
  66. package/src/libs/logger.ts +44 -0
  67. package/src/providers/aws/index.ts +1 -1
  68. package/src/providers/azure/index.ts +89 -0
  69. package/src/providers/edgestore/index.ts +9 -2
  70. package/src/providers/types.ts +1 -0
  71. package/src/types.ts +8 -0
@@ -1,5 +1,8 @@
1
+ /* eslint-disable @typescript-eslint/ban-types */
2
+ import { type z, type ZodNever } from 'zod';
1
3
  import { type AnyRouter, type Comparison } from '..';
2
- import { type Simplify } from '../../types';
4
+ import { buildPath, parsePath } from '../../adapters/shared';
5
+ import { type Prettify, type Simplify } from '../../types';
3
6
  import {
4
7
  type AnyBuilder,
5
8
  type InferBucketPathKeys,
@@ -16,6 +19,104 @@ export type GetFileRes<TBucket extends AnyBuilder> = {
16
19
  path: InferBucketPathObject<TBucket>;
17
20
  };
18
21
 
22
+ export type UploadOptions = {
23
+ /**
24
+ * e.g. 'my-file-name.jpg'
25
+ *
26
+ * By default, a unique file name will be generated for each upload.
27
+ * If you want to use a custom file name, you can use this option.
28
+ * If you use the same file name for multiple uploads, the previous file will be overwritten.
29
+ * But it might take some time for the CDN cache to be cleared.
30
+ * So maybe you will keep seeing the old file for a while.
31
+ *
32
+ * If you want to replace an existing file immediately leave the `manualFileName` option empty and use the `replaceTargetUrl` option.
33
+ */
34
+ manualFileName?: string;
35
+ /**
36
+ * Use this to replace an existing file.
37
+ * It will automatically delete the existing file when the upload is complete.
38
+ */
39
+ replaceTargetUrl?: string;
40
+ /**
41
+ * If true, the file needs to be confirmed by using the `confirmUpload` function.
42
+ * If the file is not confirmed within 24 hours, it will be deleted.
43
+ *
44
+ * This is useful for pages where the file is uploaded as soon as it is selected,
45
+ * but the user can leave the page without submitting the form.
46
+ *
47
+ * This avoids unnecessary zombie files in the bucket.
48
+ */
49
+ temporary?: boolean;
50
+ };
51
+
52
+ type TextContent = string;
53
+ type BlobContent = {
54
+ blob: Blob;
55
+ extension: string;
56
+ };
57
+ type UrlContent = {
58
+ url: string;
59
+ extension: string;
60
+ };
61
+
62
+ // type guard for `content`
63
+ function isTextContent(
64
+ content: TextContent | BlobContent | UrlContent,
65
+ ): content is TextContent {
66
+ return typeof content === 'string';
67
+ }
68
+
69
+ function isBlobContent(
70
+ content: TextContent | BlobContent | UrlContent,
71
+ ): content is BlobContent {
72
+ return typeof content !== 'string' && 'blob' in content;
73
+ }
74
+
75
+ export type UploadFileRequest<TBucket extends AnyBuilder> = {
76
+ /**
77
+ * Can be a string, a blob or an url.
78
+ *
79
+ * If it's a string, it will be converted to a blob with the type `text/plain`.
80
+ *
81
+ * @example
82
+ * // string
83
+ * content: "some text"
84
+ *
85
+ * @example
86
+ * // blob
87
+ * content: {
88
+ * blob: new Blob([text], { type: "text/csv" }),
89
+ * extension: "csv",
90
+ * }
91
+ *
92
+ * @example
93
+ * // url
94
+ * content: {
95
+ * url: "https://example.com/my-file.csv",
96
+ * extension: "csv",
97
+ * }
98
+ */
99
+ content: TextContent | BlobContent | UrlContent;
100
+ options?: UploadOptions;
101
+ } & (TBucket['$config']['ctx'] extends Record<string, never>
102
+ ? {}
103
+ : {
104
+ ctx: TBucket['$config']['ctx'];
105
+ }) &
106
+ (TBucket['_def']['input'] extends ZodNever
107
+ ? {}
108
+ : {
109
+ input: z.infer<TBucket['_def']['input']>;
110
+ });
111
+
112
+ export type UploadFileRes<TBucket extends AnyBuilder> = {
113
+ url: string;
114
+ size: number;
115
+ metadata: InferMetadataObject<TBucket>;
116
+ path: InferBucketPathObject<TBucket>;
117
+ pathOrder: (keyof InferBucketPathObject<TBucket>)[];
118
+ };
119
+
19
120
  type Filter<TBucket extends AnyBuilder> = {
20
121
  AND?: Filter<TBucket>[];
21
122
  OR?: Filter<TBucket>[];
@@ -65,24 +166,50 @@ type EdgeStoreClient<TRouter extends AnyRouter> = {
65
166
  getFile: (params: {
66
167
  url: string;
67
168
  }) => Promise<GetFileRes<TRouter['buckets'][K]>>;
68
- // TODO: replace with `upload`
69
- // requestUpload: (params: {
70
- // file: File;
71
- // path: {
72
- // [TKey in InferBucketPathKeys<TRouter['buckets'][K]>]: string;
73
- // };
74
- // metadata: InferMetadataObject<TRouter['buckets'][K]>;
75
- // replaceTargetUrl?: string;
76
- // }) => Promise<{
77
- // uploadUrl: string;
78
- // accessUrl: string;
79
- // }>;
169
+
80
170
  /**
81
- * Confirm a temporary file upload.
171
+ * Use this function to upload a file to the bucket directly from your backend.
172
+ *
173
+ * @example
174
+ * ```ts
175
+ * // simple example
176
+ * await backendClient.myBucket.upload({
177
+ * content: "some text",
178
+ * });
179
+ * ```
180
+ *
181
+ * @example
182
+ * ```ts
183
+ * // complete example
184
+ * await backendClient.myBucket.upload({
185
+ * content: {
186
+ * blob: new Blob([text], { type: "text/csv" }),
187
+ * extension: "csv",
188
+ * },
189
+ * options: {
190
+ * temporary: true,
191
+ * replaceTargetUrl: replaceUrl,
192
+ * manualFileName: "test.csv",
193
+ * },
194
+ * ctx: {
195
+ * userId: "123",
196
+ * userRole: "admin",
197
+ * },
198
+ * input: {
199
+ * type: "post",
200
+ * },
201
+ * });
202
+ * ```
203
+ */
204
+ upload: (
205
+ params: UploadFileRequest<TRouter['buckets'][K]>,
206
+ ) => Promise<Prettify<UploadFileRes<TRouter['buckets'][K]>>>;
207
+ /**
208
+ * Confirm a temporary file upload directly from your backend.
82
209
  */
83
210
  confirmUpload: (params: { url: string }) => Promise<{ success: boolean }>;
84
211
  /**
85
- * Programmatically delete a file.
212
+ * Programmatically delete a file directly from your backend.
86
213
  */
87
214
  deleteFile: (params: { url: string }) => Promise<{
88
215
  success: boolean;
@@ -95,7 +222,7 @@ type EdgeStoreClient<TRouter extends AnyRouter> = {
95
222
  */
96
223
  listFiles: (
97
224
  params?: ListFilesRequest<TRouter['buckets'][K]>,
98
- ) => Promise<ListFilesResponse<TRouter['buckets'][K]>>;
225
+ ) => Promise<Prettify<ListFilesResponse<TRouter['buckets'][K]>>>;
99
226
  };
100
227
  };
101
228
 
@@ -124,6 +251,91 @@ export function initEdgeStoreClient<TRouter extends AnyRouter>(config: {
124
251
  throw new Error(`Bucket ${bucketName} not found`);
125
252
  }
126
253
  const client: EdgeStoreClient<TRouter>[string] = {
254
+ async upload(params) {
255
+ const content = params.content;
256
+ const ctx = 'ctx' in params ? params.ctx : {};
257
+ const input = 'input' in params ? params.input : {};
258
+
259
+ const { blob, extension } = await (async () => {
260
+ if (isTextContent(content)) {
261
+ return {
262
+ blob: new Blob([content], { type: 'text/plain' }),
263
+ extension: 'txt',
264
+ };
265
+ } else if (isBlobContent(content)) {
266
+ return {
267
+ blob: content.blob,
268
+ extension: content.extension,
269
+ };
270
+ } else {
271
+ return {
272
+ blob: await getBlobFromUrl(content.url),
273
+ extension: content.extension,
274
+ };
275
+ }
276
+ })();
277
+
278
+ const path = buildPath({
279
+ bucket,
280
+ pathAttrs: {
281
+ ctx,
282
+ input,
283
+ },
284
+ fileInfo: {
285
+ type: blob.type,
286
+ size: blob.size,
287
+ extension,
288
+ temporary: false,
289
+ fileName: params.options?.manualFileName,
290
+ replaceTargetUrl: params.options?.replaceTargetUrl,
291
+ },
292
+ });
293
+ const metadata = await bucket._def.metadata({
294
+ ctx,
295
+ input,
296
+ });
297
+
298
+ const requestUploadRes = await sdk.requestUpload({
299
+ bucketName,
300
+ bucketType: bucket._def.type,
301
+ fileInfo: {
302
+ fileName: params.options?.manualFileName,
303
+ replaceTargetUrl: params.options?.replaceTargetUrl,
304
+ type: blob.type,
305
+ size: blob.size,
306
+ extension,
307
+ isPublic: bucket._def.accessControl === undefined,
308
+ temporary: params.options?.temporary ?? false,
309
+ path,
310
+ metadata,
311
+ },
312
+ });
313
+
314
+ const { signedUrl, multipart } = requestUploadRes;
315
+
316
+ if (multipart) {
317
+ // TODO
318
+ throw new Error('Multipart upload not implemented');
319
+ } else if (signedUrl) {
320
+ await fetch(signedUrl, {
321
+ method: 'PUT',
322
+ body: blob,
323
+ });
324
+ } else {
325
+ throw new Error('Missing signedUrl');
326
+ }
327
+ const { parsedPath, pathOrder } = parsePath(path);
328
+ return {
329
+ url: requestUploadRes.accessUrl,
330
+ size: blob.size,
331
+ metadata,
332
+ path: parsedPath,
333
+ pathOrder,
334
+ } satisfies UploadFileRes<typeof bucket> as UploadFileRes<
335
+ TRouter['buckets'][string]
336
+ >;
337
+ },
338
+
127
339
  async getFile(params) {
128
340
  const res = await sdk.getFile(params);
129
341
  return {
@@ -136,41 +348,6 @@ export function initEdgeStoreClient<TRouter extends AnyRouter>(config: {
136
348
  TRouter['buckets'][string]
137
349
  >;
138
350
  },
139
- // TODO: Replace with `upload`
140
- // async requestUpload(params) {
141
- // const { file, path, metadata, replaceTargetUrl } = params;
142
- // const fileExtension = file.name.includes('.')
143
- // ? file.name.split('.').pop()
144
- // : undefined;
145
- // if (!fileExtension) {
146
- // throw new Error('Missing file extension');
147
- // }
148
- // const parsedPath = Object.keys(bucket._def.path).map((key) => {
149
- // const value = path[key as keyof typeof path];
150
- // if (value === undefined) {
151
- // throw new Error(`Missing path param ${key}`);
152
- // }
153
- // return {
154
- // key,
155
- // value,
156
- // };
157
- // });
158
-
159
- // const fileInfo = {
160
- // size: file.size,
161
- // extension: fileExtension,
162
- // isPublic: bucket._def.accessControl === undefined,
163
- // path: parsedPath,
164
- // metadata,
165
- // replaceTargetUrl,
166
- // };
167
-
168
- // return await sdk.requestUpload({
169
- // bucketName,
170
- // bucketType: bucket._def.type,
171
- // fileInfo,
172
- // });
173
- // },
174
351
 
175
352
  async confirmUpload(params) {
176
353
  return await sdk.confirmUpload(params);
@@ -232,6 +409,11 @@ function getUrl(url: string, baseUrl?: string) {
232
409
  return url;
233
410
  }
234
411
 
412
+ async function getBlobFromUrl(url: string) {
413
+ const res = await fetch(url);
414
+ return await res.blob();
415
+ }
416
+
235
417
  export type InferClientResponse<TRouter extends AnyRouter> = {
236
418
  [TBucketName in keyof TRouter['buckets']]: {
237
419
  [TClienFn in keyof EdgeStoreClient<TRouter>[TBucketName]]: Simplify<
package/src/core/index.ts CHANGED
@@ -7,5 +7,11 @@ export type {
7
7
  InferBucketPathObject,
8
8
  InferMetadataObject,
9
9
  } from './internals/bucketBuilder';
10
+ export type {
11
+ EdgeStoreErrorCodeKey,
12
+ EdgeStoreErrorDetails,
13
+ EdgeStoreJsonResponse,
14
+ } from '../libs/errors/EdgeStoreError';
15
+ export { EdgeStoreApiClientError } from '../libs/errors/EdgeStoreApiClientError';
10
16
 
11
17
  export type AnyRouter = EdgeStoreRouter<any>;
@@ -1,5 +1,6 @@
1
1
  import { type AnyRouter } from '..';
2
2
  import EdgeStoreCredentialsError from '../../libs/errors/EdgeStoreCredentialsError';
3
+ import EdgeStoreError from '../../libs/errors/EdgeStoreError';
3
4
  import { type AnyContext, type AnyMetadata } from '../internals/bucketBuilder';
4
5
 
5
6
  const API_ENDPOINT =
@@ -93,7 +94,12 @@ export const edgeStoreRawSdk = {
93
94
  path: bucket._def.path.map((p: { [key: string]: () => string }) => {
94
95
  const paramEntries = Object.entries(p);
95
96
  if (paramEntries[0] === undefined) {
96
- throw new Error('Missing path param');
97
+ throw new EdgeStoreError({
98
+ message: `Empty path param found in: ${JSON.stringify(
99
+ bucket._def.path,
100
+ )}`,
101
+ code: 'SERVER_ERROR',
102
+ });
97
103
  }
98
104
  const [key, value] = paramEntries[0];
99
105
  return {
@@ -0,0 +1,14 @@
1
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
+
3
+ import { type EdgeStoreJsonResponse } from './EdgeStoreError';
4
+
5
+ export class EdgeStoreApiClientError extends Error {
6
+ public readonly data: EdgeStoreJsonResponse;
7
+
8
+ constructor(opts: { response: EdgeStoreJsonResponse }) {
9
+ super(opts.response.message);
10
+ this.name = 'EdgeStoreApiClientError';
11
+
12
+ this.data = opts.response;
13
+ }
14
+ }
@@ -1,6 +1,5 @@
1
1
  const DEFAULT_MESSAGE = `Missing EDGE_STORE_ACCESS_KEY or EDGE_STORE_SECRET_KEY.
2
- This can happen if you are trying to use the vanilla client in your frontend.
3
- The vanilla client should only be used in the backend.`;
2
+ This can happen if you are trying to import something related to the backend of Edge Store in a client component.`;
4
3
 
5
4
  class EdgeStoreCredentialsError extends Error {
6
5
  constructor(message = DEFAULT_MESSAGE) {
@@ -1,24 +1,91 @@
1
+ import { type Simplify } from '../../types';
2
+
3
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
1
4
  export const EDGE_STORE_ERROR_CODES = {
2
5
  BAD_REQUEST: 400,
6
+ FILE_TOO_LARGE: 400,
7
+ MIME_TYPE_NOT_ALLOWED: 400,
3
8
  UNAUTHORIZED: 401,
9
+ UPLOAD_NOT_ALLOWED: 403,
10
+ DELETE_NOT_ALLOWED: 403,
11
+ CREATE_CONTEXT_ERROR: 500,
12
+ SERVER_ERROR: 500,
4
13
  } as const;
5
14
 
6
15
  export type EdgeStoreErrorCodeKey = keyof typeof EDGE_STORE_ERROR_CODES;
7
16
 
8
- class EdgeStoreError extends Error {
17
+ export type EdgeStoreErrorDetails<TCode extends EdgeStoreErrorCodeKey> =
18
+ TCode extends 'FILE_TOO_LARGE'
19
+ ? {
20
+ maxFileSize: number;
21
+ fileSize: number;
22
+ }
23
+ : TCode extends 'MIME_TYPE_NOT_ALLOWED'
24
+ ? {
25
+ allowedMimeTypes: string[];
26
+ mimeType: string;
27
+ }
28
+ : never;
29
+
30
+ export type EdgeStoreJsonResponse = Simplify<
31
+ | {
32
+ message: string;
33
+ code: 'FILE_TOO_LARGE';
34
+ details: EdgeStoreErrorDetails<'FILE_TOO_LARGE'>;
35
+ }
36
+ | {
37
+ message: string;
38
+ code: 'MIME_TYPE_NOT_ALLOWED';
39
+ details: EdgeStoreErrorDetails<'MIME_TYPE_NOT_ALLOWED'>;
40
+ }
41
+ | {
42
+ message: string;
43
+ code: Exclude<
44
+ EdgeStoreErrorCodeKey,
45
+ 'FILE_TOO_LARGE' | 'MIME_TYPE_NOT_ALLOWED'
46
+ >;
47
+ }
48
+ >;
49
+
50
+ class EdgeStoreError<TCode extends EdgeStoreErrorCodeKey> extends Error {
9
51
  public readonly cause?: Error;
10
- public readonly code: EdgeStoreErrorCodeKey;
52
+ public readonly code: TCode;
53
+ public readonly level: 'error' | 'warn';
54
+ public readonly details: EdgeStoreErrorDetails<TCode>;
11
55
 
12
- constructor(opts: {
13
- message: string;
14
- code: EdgeStoreErrorCodeKey;
15
- cause?: Error;
16
- }) {
56
+ constructor(
57
+ opts: {
58
+ message: string;
59
+ code: TCode;
60
+ cause?: Error;
61
+ } & (EdgeStoreErrorDetails<TCode> extends undefined
62
+ ? object
63
+ : {
64
+ details: EdgeStoreErrorDetails<TCode>;
65
+ }),
66
+ ) {
17
67
  super(opts.message);
18
68
  this.name = 'EdgeStoreError';
19
69
 
20
70
  this.code = opts.code;
21
71
  this.cause = opts.cause;
72
+ this.level = EDGE_STORE_ERROR_CODES[opts.code] >= 500 ? 'error' : 'warn';
73
+ this.details = 'details' in opts ? opts.details : undefined!;
74
+ }
75
+
76
+ formattedMessage(): string {
77
+ return `${this.message}${
78
+ this.details ? `\n Details: ${JSON.stringify(this.details)}` : ''
79
+ }${this.cause ? `\n Caused by: ${this.cause.message}` : ''}`;
80
+ }
81
+
82
+ formattedJson(): EdgeStoreJsonResponse {
83
+ return {
84
+ message:
85
+ this.code === 'SERVER_ERROR' ? 'Internal server error' : this.message,
86
+ code: this.code,
87
+ details: this.details as any,
88
+ } satisfies EdgeStoreJsonResponse;
22
89
  }
23
90
  }
24
91
 
@@ -0,0 +1,44 @@
1
+ /* eslint-disable no-console */
2
+
3
+ const logLevel = ['debug', 'info', 'warn', 'error', 'none'] as const;
4
+
5
+ export type LogLevel = typeof logLevel[number];
6
+
7
+ class Logger {
8
+ private logLevel: LogLevel;
9
+
10
+ constructor(logLevel?: LogLevel) {
11
+ this.logLevel =
12
+ logLevel ?? (process.env.NODE_ENV === 'production' ? 'error' : 'info');
13
+ }
14
+
15
+ private shouldLog(level: LogLevel): boolean {
16
+ return logLevel.indexOf(level) >= logLevel.indexOf(this.logLevel);
17
+ }
18
+
19
+ debug(message?: any, ...optionalParams: any[]): void {
20
+ if (this.shouldLog('debug')) {
21
+ console.debug('[EdgeStoreDebug]', message, ...optionalParams);
22
+ }
23
+ }
24
+
25
+ info(message?: any, ...optionalParams: any[]): void {
26
+ if (this.shouldLog('info')) {
27
+ console.info('[EdgeStoreInfo]', message, ...optionalParams);
28
+ }
29
+ }
30
+
31
+ warn(message?: any, ...optionalParams: any[]): void {
32
+ if (this.shouldLog('warn')) {
33
+ console.warn('[EdgeStoreWarn]', message, ...optionalParams);
34
+ }
35
+ }
36
+
37
+ error(message?: any, ...optionalParams: any[]): void {
38
+ if (this.shouldLog('error')) {
39
+ console.error('[EdgeStoreError]', message, ...optionalParams);
40
+ }
41
+ }
42
+ }
43
+
44
+ export default Logger;
@@ -107,7 +107,7 @@ export function AWSProvider(options?: AWSProviderOptions): Provider {
107
107
  const extension = fileInfo.extension
108
108
  ? `.${fileInfo.extension.replace('.', '')}`
109
109
  : '';
110
- const fileName = `${nameId}${extension}`;
110
+ const fileName = fileInfo.fileName ?? `${nameId}${extension}`;
111
111
  const filePath = fileInfo.path.reduce((acc, item) => {
112
112
  return `${acc}/${item.value}`;
113
113
  }, '');
@@ -0,0 +1,89 @@
1
+ import { BlobServiceClient } from '@azure/storage-blob';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ import { type Provider } from '../types';
4
+
5
+ export type AzureProviderOptions = {
6
+ /**
7
+ * The storage account name for Azure Blob Storage
8
+ * Can also be set via the `ES_AZURE_ACCOUNT_NAME` environment variable.
9
+ */
10
+ storageAccountName?: string;
11
+ /**
12
+ * SAS token for Azure Blob Storage
13
+ * Can also be set via the `ES_AZURE_SAS_TOKEN` environment variable.
14
+ */
15
+ sasToken?: string;
16
+ /**
17
+ * Azure Blob Storage container name
18
+ * Can also be set via the `ES_AZURE_CONTAINER_NAME` environment variable.
19
+ */
20
+ containerName?: string;
21
+ };
22
+
23
+ export function AzureProvider(options?: AzureProviderOptions): Provider {
24
+ const {
25
+ storageAccountName = process.env.ES_AZURE_ACCOUNT_NAME,
26
+ sasToken = process.env.ES_AZURE_SAS_TOKEN,
27
+ containerName = process.env.ES_AZURE_CONTAINER_NAME,
28
+ } = options ?? {};
29
+
30
+ const baseUrl = `https://${storageAccountName}.blob.core.windows.net`;
31
+ const blobServiceClient = new BlobServiceClient(`${baseUrl}?${sasToken}`);
32
+ const containerClient = blobServiceClient.getContainerClient(
33
+ containerName ?? '',
34
+ );
35
+
36
+ return {
37
+ async init() {
38
+ return {};
39
+ },
40
+ getBaseUrl() {
41
+ return baseUrl;
42
+ },
43
+ async getFile({ url }) {
44
+ const blobClient = containerClient.getBlobClient(url);
45
+
46
+ const { contentLength, lastModified } = await blobClient.getProperties();
47
+
48
+ return {
49
+ url: url,
50
+ metadata: {},
51
+ path: {},
52
+ size: contentLength ?? 0,
53
+ uploadedAt: lastModified ?? new Date(),
54
+ };
55
+ },
56
+ async requestUpload({ fileInfo }) {
57
+ const nameId = uuidv4();
58
+ const extension = fileInfo.extension
59
+ ? `.${fileInfo.extension.replace('.', '')}`
60
+ : '';
61
+ const fileName = fileInfo.fileName ?? `${nameId}${extension}`;
62
+
63
+ const blobClient = containerClient.getBlobClient(fileName);
64
+
65
+ const url = blobClient.url;
66
+
67
+ return {
68
+ uploadUrl: url,
69
+ accessUrl: url,
70
+ };
71
+ },
72
+ async requestUploadParts() {
73
+ throw new Error('Not implemented');
74
+ },
75
+ async completeMultipartUpload() {
76
+ throw new Error('Not implemented');
77
+ },
78
+ async confirmUpload() {
79
+ throw new Error('Not implemented');
80
+ },
81
+ async deleteFile({ url }) {
82
+ const blobClient = containerClient.getBlobClient(url);
83
+ await blobClient.delete();
84
+ return {
85
+ success: true,
86
+ };
87
+ },
88
+ };
89
+ }
@@ -1,5 +1,6 @@
1
1
  import { initEdgeStoreSdk } from '../../core/sdk';
2
2
  import EdgeStoreCredentialsError from '../../libs/errors/EdgeStoreCredentialsError';
3
+ import EdgeStoreError from '../../libs/errors/EdgeStoreError';
3
4
  import { type Provider, type RequestUploadRes } from '../types';
4
5
 
5
6
  const DEFAULT_BASE_URL = 'https://files.edgestore.dev';
@@ -111,7 +112,10 @@ export function EdgeStoreProvider(
111
112
  thumbnailUrl: res.thumbnailUrl,
112
113
  };
113
114
  } else {
114
- throw new Error('Could not get upload url');
115
+ throw new EdgeStoreError({
116
+ message: 'Could not get upload url',
117
+ code: 'SERVER_ERROR',
118
+ });
115
119
  }
116
120
  }
117
121
  const res = await edgeStoreSdk.requestUpload({
@@ -126,7 +130,10 @@ export function EdgeStoreProvider(
126
130
  thumbnailUrl: res.thumbnailUrl,
127
131
  };
128
132
  }
129
- throw new Error('Could not get upload url');
133
+ throw new EdgeStoreError({
134
+ message: 'Could not get upload url',
135
+ code: 'SERVER_ERROR',
136
+ });
130
137
  },
131
138
  requestUploadParts: async ({ multipart, path }) => {
132
139
  const res = await edgeStoreSdk.requestUploadParts({
@@ -41,6 +41,7 @@ export type RequestUploadParams = {
41
41
  size: number;
42
42
  extension: string;
43
43
  isPublic: boolean;
44
+ fileName?: string;
44
45
  path: {
45
46
  key: string;
46
47
  value: string;
package/src/types.ts CHANGED
@@ -61,6 +61,14 @@ export type Simplify<TType> = TType extends any[] | Date
61
61
  ? TType
62
62
  : { [K in keyof TType]: TType[K] };
63
63
 
64
+ /**
65
+ * @internal
66
+ */
67
+ export type Prettify<TType> = {
68
+ [K in keyof TType]: TType[K];
69
+ // eslint-disable-next-line @typescript-eslint/ban-types
70
+ } & {};
71
+
64
72
  /**
65
73
  * @public
66
74
  */