@edgestore/server 0.1.5-alpha.3 → 0.1.5-alpha.5

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 (52) hide show
  1. package/dist/adapters/next/app/index.d.ts +5 -3
  2. package/dist/adapters/next/app/index.d.ts.map +1 -1
  3. package/dist/adapters/next/app/index.js +38 -15
  4. package/dist/adapters/next/app/index.mjs +37 -14
  5. package/dist/adapters/next/pages/index.d.ts +5 -3
  6. package/dist/adapters/next/pages/index.d.ts.map +1 -1
  7. package/dist/adapters/next/pages/index.js +32 -12
  8. package/dist/adapters/next/pages/index.mjs +31 -11
  9. package/dist/adapters/shared.d.ts.map +1 -1
  10. package/dist/core/client/index.d.ts +56 -2
  11. package/dist/core/client/index.d.ts.map +1 -1
  12. package/dist/core/index.d.ts +2 -0
  13. package/dist/core/index.d.ts.map +1 -1
  14. package/dist/core/index.js +13 -3
  15. package/dist/core/index.mjs +14 -5
  16. package/dist/core/sdk/index.d.ts.map +1 -1
  17. package/dist/{index-50ab9e08.js → index-7cb3a3f3.mjs} +41 -5
  18. package/dist/{index-f33a00fb.js → index-9eb6248c.js} +37 -2
  19. package/dist/{index-30a3741e.mjs → index-e25c8209.js} +48 -2
  20. package/dist/libs/errors/EdgeStoreApiClientError.d.ts +8 -0
  21. package/dist/libs/errors/EdgeStoreApiClientError.d.ts.map +1 -0
  22. package/dist/libs/errors/EdgeStoreError.d.ts +36 -4
  23. package/dist/libs/errors/EdgeStoreError.d.ts.map +1 -1
  24. package/dist/libs/logger.d.ts +13 -0
  25. package/dist/libs/logger.d.ts.map +1 -0
  26. package/dist/logger-0f08f252.mjs +40 -0
  27. package/dist/logger-623f2833.js +42 -0
  28. package/dist/logger-8f098618.js +33 -0
  29. package/dist/providers/azure/index.d.ts +20 -0
  30. package/dist/providers/azure/index.d.ts.map +1 -0
  31. package/dist/providers/azure/index.js +61 -0
  32. package/dist/providers/azure/index.mjs +57 -0
  33. package/dist/providers/edgestore/index.d.ts.map +1 -1
  34. package/dist/providers/edgestore/index.js +10 -3
  35. package/dist/providers/edgestore/index.mjs +10 -3
  36. package/dist/{shared-5d1e7f43.js → shared-53cb59dd.js} +72 -51
  37. package/dist/{shared-30b7a2ab.mjs → shared-b14a84ee.mjs} +65 -42
  38. package/dist/{shared-88655ba7.js → shared-f8ddbf7c.js} +62 -36
  39. package/package.json +12 -2
  40. package/providers/azure/index.d.ts +1 -0
  41. package/providers/azure/index.js +1 -0
  42. package/src/adapters/next/app/index.ts +52 -19
  43. package/src/adapters/next/pages/index.ts +43 -14
  44. package/src/adapters/shared.ts +62 -29
  45. package/src/core/client/index.ts +57 -2
  46. package/src/core/index.ts +6 -0
  47. package/src/core/sdk/index.ts +7 -1
  48. package/src/libs/errors/EdgeStoreApiClientError.ts +14 -0
  49. package/src/libs/errors/EdgeStoreError.ts +76 -7
  50. package/src/libs/logger.ts +44 -0
  51. package/src/providers/azure/index.ts +89 -0
  52. package/src/providers/edgestore/index.ts +9 -2
@@ -50,6 +50,26 @@ export type UploadOptions = {
50
50
  };
51
51
 
52
52
  export type UploadFileRequest<TBucket extends AnyBuilder> = {
53
+ /**
54
+ * Can be a string or a blob.
55
+ *
56
+ * If it's a string, it will be converted to a blob with the type `text/plain`.
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * // string
61
+ * content: "some text"
62
+ * ```
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * // blob
67
+ * content: {
68
+ * blob: new Blob([text], { type: "text/csv" }),
69
+ * extension: "csv",
70
+ * }
71
+ * ```
72
+ */
53
73
  content:
54
74
  | string
55
75
  | {
@@ -125,15 +145,50 @@ type EdgeStoreClient<TRouter extends AnyRouter> = {
125
145
  getFile: (params: {
126
146
  url: string;
127
147
  }) => Promise<GetFileRes<TRouter['buckets'][K]>>;
148
+
149
+ /**
150
+ * Use this function to upload a file to the bucket directly from your backend.
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * // simple example
155
+ * await backendClient.myBucket.upload({
156
+ * content: "some text",
157
+ * });
158
+ * ```
159
+ *
160
+ * @example
161
+ * ```ts
162
+ * // complete example
163
+ * await backendClient.myBucket.upload({
164
+ * content: {
165
+ * blob: new Blob([text], { type: "text/csv" }),
166
+ * extension: "csv",
167
+ * },
168
+ * options: {
169
+ * temporary: true,
170
+ * replaceTargetUrl: replaceUrl,
171
+ * manualFileName: "test.csv",
172
+ * },
173
+ * ctx: {
174
+ * userId: "123",
175
+ * userRole: "admin",
176
+ * },
177
+ * input: {
178
+ * type: "post",
179
+ * },
180
+ * });
181
+ * ```
182
+ */
128
183
  upload: (
129
184
  params: UploadFileRequest<TRouter['buckets'][K]>,
130
185
  ) => Promise<Prettify<UploadFileRes<TRouter['buckets'][K]>>>;
131
186
  /**
132
- * Confirm a temporary file upload.
187
+ * Confirm a temporary file upload directly from your backend.
133
188
  */
134
189
  confirmUpload: (params: { url: string }) => Promise<{ success: boolean }>;
135
190
  /**
136
- * Programmatically delete a file.
191
+ * Programmatically delete a file directly from your backend.
137
192
  */
138
193
  deleteFile: (params: { url: string }) => Promise<{
139
194
  success: boolean;
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;
@@ -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({