@edgestore/server 0.1.5-alpha.4 → 0.1.5-alpha.6

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 (47) hide show
  1. package/adapters/express/index.d.ts +1 -0
  2. package/adapters/express/index.js +1 -0
  3. package/dist/adapters/next/app/index.d.ts +5 -3
  4. package/dist/adapters/next/app/index.d.ts.map +1 -1
  5. package/dist/adapters/next/app/index.js +38 -15
  6. package/dist/adapters/next/app/index.mjs +37 -14
  7. package/dist/adapters/next/pages/index.d.ts +5 -3
  8. package/dist/adapters/next/pages/index.d.ts.map +1 -1
  9. package/dist/adapters/next/pages/index.js +32 -12
  10. package/dist/adapters/next/pages/index.mjs +31 -11
  11. package/dist/adapters/shared.d.ts.map +1 -1
  12. package/dist/core/client/index.d.ts +75 -7
  13. package/dist/core/client/index.d.ts.map +1 -1
  14. package/dist/core/index.d.ts +2 -0
  15. package/dist/core/index.d.ts.map +1 -1
  16. package/dist/core/index.js +32 -6
  17. package/dist/core/index.mjs +33 -8
  18. package/dist/core/sdk/index.d.ts.map +1 -1
  19. package/dist/{index-50ab9e08.js → index-7cb3a3f3.mjs} +41 -5
  20. package/dist/{index-f33a00fb.js → index-9eb6248c.js} +37 -2
  21. package/dist/{index-30a3741e.mjs → index-e25c8209.js} +48 -2
  22. package/dist/libs/errors/EdgeStoreApiClientError.d.ts +8 -0
  23. package/dist/libs/errors/EdgeStoreApiClientError.d.ts.map +1 -0
  24. package/dist/libs/errors/EdgeStoreError.d.ts +36 -4
  25. package/dist/libs/errors/EdgeStoreError.d.ts.map +1 -1
  26. package/dist/libs/logger.d.ts +13 -0
  27. package/dist/libs/logger.d.ts.map +1 -0
  28. package/dist/logger-0f08f252.mjs +40 -0
  29. package/dist/logger-623f2833.js +42 -0
  30. package/dist/logger-8f098618.js +33 -0
  31. package/dist/providers/edgestore/index.d.ts.map +1 -1
  32. package/dist/providers/edgestore/index.js +10 -3
  33. package/dist/providers/edgestore/index.mjs +10 -3
  34. package/dist/{shared-5d1e7f43.js → shared-53cb59dd.js} +72 -51
  35. package/dist/{shared-30b7a2ab.mjs → shared-b14a84ee.mjs} +65 -42
  36. package/dist/{shared-88655ba7.js → shared-f8ddbf7c.js} +62 -36
  37. package/package.json +2 -2
  38. package/src/adapters/next/app/index.ts +51 -19
  39. package/src/adapters/next/pages/index.ts +42 -14
  40. package/src/adapters/shared.ts +61 -29
  41. package/src/core/client/index.ts +103 -12
  42. package/src/core/index.ts +6 -0
  43. package/src/core/sdk/index.ts +7 -1
  44. package/src/libs/errors/EdgeStoreApiClientError.ts +14 -0
  45. package/src/libs/errors/EdgeStoreError.ts +76 -7
  46. package/src/libs/logger.ts +44 -0
  47. package/src/providers/edgestore/index.ts +9 -2
@@ -49,13 +49,59 @@ export type UploadOptions = {
49
49
  temporary?: boolean;
50
50
  };
51
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
+
52
75
  export type UploadFileRequest<TBucket extends AnyBuilder> = {
53
- content:
54
- | string
55
- | {
56
- blob: Blob;
57
- extension: string;
58
- };
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
+ * ```ts
83
+ * // string
84
+ * content: "some text"
85
+ * ```
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * // blob
90
+ * content: {
91
+ * blob: new Blob([text], { type: "text/csv" }),
92
+ * extension: "csv",
93
+ * }
94
+ * ```
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * // url
99
+ * content: {
100
+ * url: "https://example.com/my-file.csv",
101
+ * }
102
+ * ```
103
+ */
104
+ content: TextContent | BlobContent | UrlContent;
59
105
  options?: UploadOptions;
60
106
  } & (TBucket['$config']['ctx'] extends Record<string, never>
61
107
  ? {}
@@ -125,15 +171,50 @@ type EdgeStoreClient<TRouter extends AnyRouter> = {
125
171
  getFile: (params: {
126
172
  url: string;
127
173
  }) => Promise<GetFileRes<TRouter['buckets'][K]>>;
174
+
175
+ /**
176
+ * Use this function to upload a file to the bucket directly from your backend.
177
+ *
178
+ * @example
179
+ * ```ts
180
+ * // simple example
181
+ * await backendClient.myBucket.upload({
182
+ * content: "some text",
183
+ * });
184
+ * ```
185
+ *
186
+ * @example
187
+ * ```ts
188
+ * // complete example
189
+ * await backendClient.myBucket.upload({
190
+ * content: {
191
+ * blob: new Blob([text], { type: "text/csv" }),
192
+ * extension: "csv",
193
+ * },
194
+ * options: {
195
+ * temporary: true,
196
+ * replaceTargetUrl: replaceUrl,
197
+ * manualFileName: "test.csv",
198
+ * },
199
+ * ctx: {
200
+ * userId: "123",
201
+ * userRole: "admin",
202
+ * },
203
+ * input: {
204
+ * type: "post",
205
+ * },
206
+ * });
207
+ * ```
208
+ */
128
209
  upload: (
129
210
  params: UploadFileRequest<TRouter['buckets'][K]>,
130
211
  ) => Promise<Prettify<UploadFileRes<TRouter['buckets'][K]>>>;
131
212
  /**
132
- * Confirm a temporary file upload.
213
+ * Confirm a temporary file upload directly from your backend.
133
214
  */
134
215
  confirmUpload: (params: { url: string }) => Promise<{ success: boolean }>;
135
216
  /**
136
- * Programmatically delete a file.
217
+ * Programmatically delete a file directly from your backend.
137
218
  */
138
219
  deleteFile: (params: { url: string }) => Promise<{
139
220
  success: boolean;
@@ -146,7 +227,7 @@ type EdgeStoreClient<TRouter extends AnyRouter> = {
146
227
  */
147
228
  listFiles: (
148
229
  params?: ListFilesRequest<TRouter['buckets'][K]>,
149
- ) => Promise<ListFilesResponse<TRouter['buckets'][K]>>;
230
+ ) => Promise<Prettify<ListFilesResponse<TRouter['buckets'][K]>>>;
150
231
  };
151
232
  };
152
233
 
@@ -180,17 +261,22 @@ export function initEdgeStoreClient<TRouter extends AnyRouter>(config: {
180
261
  const ctx = 'ctx' in params ? params.ctx : {};
181
262
  const input = 'input' in params ? params.input : {};
182
263
 
183
- const { blob, extension } = (() => {
184
- if (typeof content === 'string') {
264
+ const { blob, extension } = await (async () => {
265
+ if (isTextContent(content)) {
185
266
  return {
186
267
  blob: new Blob([content], { type: 'text/plain' }),
187
268
  extension: 'txt',
188
269
  };
189
- } else {
270
+ } else if (isBlobContent(content)) {
190
271
  return {
191
272
  blob: content.blob,
192
273
  extension: content.extension,
193
274
  };
275
+ } else {
276
+ return {
277
+ blob: await getBlobFromUrl(content.url),
278
+ extension: content.extension,
279
+ };
194
280
  }
195
281
  })();
196
282
 
@@ -328,6 +414,11 @@ function getUrl(url: string, baseUrl?: string) {
328
414
  return url;
329
415
  }
330
416
 
417
+ async function getBlobFromUrl(url: string) {
418
+ const res = await fetch(url);
419
+ return await res.blob();
420
+ }
421
+
331
422
  export type InferClientResponse<TRouter extends AnyRouter> = {
332
423
  [TBucketName in keyof TRouter['buckets']]: {
333
424
  [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,24 +1,93 @@
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 `EdgeStore${this.level === 'error' ? 'Error' : 'Info'}: ${
78
+ this.message
79
+ }${this.details ? `\n Details: ${JSON.stringify(this.details)}` : ''}${
80
+ this.cause ? `\n Caused by: ${this.cause.message}` : ''
81
+ }`;
82
+ }
83
+
84
+ formattedJson(): EdgeStoreJsonResponse {
85
+ return {
86
+ message:
87
+ this.code === 'SERVER_ERROR' ? 'Internal server error' : this.message,
88
+ code: this.code,
89
+ details: this.details as any,
90
+ } satisfies EdgeStoreJsonResponse;
22
91
  }
23
92
  }
24
93
 
@@ -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(message, ...optionalParams);
22
+ }
23
+ }
24
+
25
+ info(message?: any, ...optionalParams: any[]): void {
26
+ if (this.shouldLog('info')) {
27
+ console.info(message, ...optionalParams);
28
+ }
29
+ }
30
+
31
+ warn(message?: any, ...optionalParams: any[]): void {
32
+ if (this.shouldLog('warn')) {
33
+ console.warn(message, ...optionalParams);
34
+ }
35
+ }
36
+
37
+ error(message?: any, ...optionalParams: any[]): void {
38
+ if (this.shouldLog('error')) {
39
+ console.error(message, ...optionalParams);
40
+ }
41
+ }
42
+ }
43
+
44
+ export default Logger;
@@ -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({