@edgestore/server 0.4.0 → 0.5.0

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 (68) hide show
  1. package/adapters/astro/index.d.ts +1 -0
  2. package/adapters/astro/index.js +1 -0
  3. package/adapters/hono/index.d.ts +1 -0
  4. package/adapters/hono/index.js +1 -0
  5. package/adapters/remix/index.d.ts +1 -0
  6. package/adapters/remix/index.js +1 -0
  7. package/dist/adapters/astro/index.d.ts +14 -0
  8. package/dist/adapters/astro/index.d.ts.map +1 -0
  9. package/dist/adapters/astro/index.js +183 -0
  10. package/dist/adapters/astro/index.mjs +179 -0
  11. package/dist/adapters/express/index.js +3 -3
  12. package/dist/adapters/express/index.mjs +3 -3
  13. package/dist/adapters/fastify/index.js +3 -3
  14. package/dist/adapters/fastify/index.mjs +3 -3
  15. package/dist/adapters/hono/index.d.ts +82 -0
  16. package/dist/adapters/hono/index.d.ts.map +1 -0
  17. package/dist/adapters/hono/index.js +137 -0
  18. package/dist/adapters/hono/index.mjs +133 -0
  19. package/dist/adapters/next/app/index.js +3 -3
  20. package/dist/adapters/next/app/index.mjs +3 -3
  21. package/dist/adapters/next/pages/index.js +3 -3
  22. package/dist/adapters/next/pages/index.mjs +3 -3
  23. package/dist/adapters/remix/index.d.ts +18 -0
  24. package/dist/adapters/remix/index.d.ts.map +1 -0
  25. package/dist/adapters/remix/index.js +158 -0
  26. package/dist/adapters/remix/index.mjs +154 -0
  27. package/dist/adapters/shared.d.ts +2 -0
  28. package/dist/adapters/shared.d.ts.map +1 -1
  29. package/dist/adapters/start/index.js +3 -3
  30. package/dist/adapters/start/index.mjs +3 -3
  31. package/dist/core/index.js +3 -3
  32. package/dist/core/index.mjs +4 -4
  33. package/dist/core/sdk/index.d.ts.map +1 -1
  34. package/dist/{index-a7cc3cd3.mjs → index-2848cb40.mjs} +3 -2
  35. package/dist/{index-474a21c4.js → index-421c502f.js} +3 -2
  36. package/dist/{index-b689bf59.js → index-7b259533.js} +5 -4
  37. package/dist/libs/logger.d.ts +1 -1
  38. package/dist/libs/logger.d.ts.map +1 -1
  39. package/dist/providers/aws/index.d.ts.map +1 -1
  40. package/dist/providers/aws/index.js +7 -2
  41. package/dist/providers/aws/index.mjs +7 -2
  42. package/dist/providers/azure/index.d.ts.map +1 -1
  43. package/dist/providers/azure/index.js +6 -1
  44. package/dist/providers/azure/index.mjs +6 -1
  45. package/dist/providers/edgestore/index.d.ts.map +1 -1
  46. package/dist/providers/edgestore/index.js +13 -6
  47. package/dist/providers/edgestore/index.mjs +10 -3
  48. package/dist/{shared-d27101a7.js → shared-25dbfab4.js} +17 -4
  49. package/dist/{shared-c9442cbb.mjs → shared-4b199b96.mjs} +16 -4
  50. package/dist/{shared-6f6fd0bd.js → shared-685c8a0c.js} +17 -3
  51. package/dist/{utils-5819d5e1.js → utils-0aab6e3b.js} +3 -1
  52. package/dist/{utils-f6f56d38.mjs → utils-7349adab.mjs} +3 -1
  53. package/dist/{utils-461a2e3b.js → utils-b3d35894.js} +3 -2
  54. package/package.json +20 -3
  55. package/src/adapters/astro/index.ts +222 -0
  56. package/src/adapters/hono/index.ts +195 -0
  57. package/src/adapters/remix/index.ts +201 -0
  58. package/src/adapters/shared.ts +20 -3
  59. package/src/core/client/index.ts +2 -2
  60. package/src/core/sdk/index.ts +4 -3
  61. package/src/libs/logger.ts +4 -3
  62. package/src/providers/aws/index.ts +8 -7
  63. package/src/providers/azure/index.ts +5 -4
  64. package/src/providers/edgestore/index.ts +8 -3
  65. package/adapters/index.d.ts +0 -1
  66. package/adapters/index.js +0 -1
  67. package/providers/index.d.ts +0 -1
  68. package/providers/index.js +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edgestore/server",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
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",
@@ -37,6 +37,11 @@
37
37
  "require": "./dist/core/index.js",
38
38
  "default": "./dist/core/index.js"
39
39
  },
40
+ "./adapters/astro": {
41
+ "import": "./dist/adapters/astro/index.mjs",
42
+ "require": "./dist/adapters/astro/index.js",
43
+ "default": "./dist/adapters/astro/index.js"
44
+ },
40
45
  "./adapters/express": {
41
46
  "import": "./dist/adapters/express/index.mjs",
42
47
  "require": "./dist/adapters/express/index.js",
@@ -47,6 +52,11 @@
47
52
  "require": "./dist/adapters/fastify/index.js",
48
53
  "default": "./dist/adapters/fastify/index.js"
49
54
  },
55
+ "./adapters/hono": {
56
+ "import": "./dist/adapters/hono/index.mjs",
57
+ "require": "./dist/adapters/hono/index.js",
58
+ "default": "./dist/adapters/hono/index.js"
59
+ },
50
60
  "./adapters/next/pages": {
51
61
  "import": "./dist/adapters/next/pages/index.mjs",
52
62
  "require": "./dist/adapters/next/pages/index.js",
@@ -57,6 +67,11 @@
57
67
  "require": "./dist/adapters/next/app/index.js",
58
68
  "default": "./dist/adapters/next/app/index.js"
59
69
  },
70
+ "./adapters/remix": {
71
+ "import": "./dist/adapters/remix/index.mjs",
72
+ "require": "./dist/adapters/remix/index.js",
73
+ "default": "./dist/adapters/remix/index.js"
74
+ },
60
75
  "./adapters/start": {
61
76
  "import": "./dist/adapters/start/index.mjs",
62
77
  "require": "./dist/adapters/start/index.js",
@@ -94,7 +109,7 @@
94
109
  },
95
110
  "license": "MIT",
96
111
  "dependencies": {
97
- "@edgestore/shared": "0.4.0",
112
+ "@edgestore/shared": "0.5.0",
98
113
  "@panva/hkdf": "^1.0.4",
99
114
  "cookie": "^0.5.0",
100
115
  "jose": "^4.13.1",
@@ -127,12 +142,14 @@
127
142
  "@types/cookie": "^0.5.1",
128
143
  "@types/node": "^20",
129
144
  "@types/uuid": "^9.0.1",
145
+ "astro": "^5.7.12",
130
146
  "fastify": "^5.3.2",
147
+ "hono": "^4.7.9",
131
148
  "next": "^15.2.4",
132
149
  "react": "^19.0.0",
133
150
  "react-dom": "^19.0.0",
134
151
  "typescript": "^5",
135
152
  "zod": "3.21.4"
136
153
  },
137
- "gitHead": "63d6ba86e038a61bfbf506385183482c91e80842"
154
+ "gitHead": "1bc997b2797e026d6661b9964e74c6e117dba352"
138
155
  }
@@ -0,0 +1,222 @@
1
+ import {
2
+ EDGE_STORE_ERROR_CODES,
3
+ EdgeStoreError,
4
+ type EdgeStoreErrorCodeKey,
5
+ type EdgeStoreRouter,
6
+ type MaybePromise,
7
+ type Provider,
8
+ } from '@edgestore/shared';
9
+ import type { APIContext } from 'astro';
10
+ import Logger, { type LogLevel } from '../../libs/logger';
11
+ import { matchPath } from '../../libs/utils';
12
+ import { EdgeStoreProvider } from '../../providers/edgestore';
13
+ import {
14
+ completeMultipartUpload,
15
+ confirmUpload,
16
+ deleteFile,
17
+ init,
18
+ requestUpload,
19
+ requestUploadParts,
20
+ type CompleteMultipartUploadBody,
21
+ type ConfirmUploadBody,
22
+ type DeleteFileBody,
23
+ type RequestUploadBody,
24
+ type RequestUploadPartsParams,
25
+ } from '../shared';
26
+
27
+ export type Config<TCtx> = {
28
+ provider?: Provider;
29
+ router: EdgeStoreRouter<TCtx>;
30
+ logLevel?: LogLevel;
31
+ } & (TCtx extends Record<string, never>
32
+ ? object
33
+ : {
34
+ provider?: Provider;
35
+ router: EdgeStoreRouter<TCtx>;
36
+ createContext: (opts: APIContext) => MaybePromise<TCtx>;
37
+ });
38
+
39
+ declare const globalThis: {
40
+ _EDGE_STORE_LOGGER: Logger;
41
+ };
42
+
43
+ // Helper to safely get cookies from Astro request
44
+ function getCookie(request: Request, name: string): string | undefined {
45
+ const cookieHeader = request.headers.get('cookie');
46
+ if (!cookieHeader) return undefined;
47
+
48
+ const cookies = cookieHeader
49
+ .split(';')
50
+ .reduce<Record<string, string>>((acc, cookie) => {
51
+ const [key, value] = cookie.trim().split('=');
52
+ if (key && value) acc[key] = value;
53
+ return acc;
54
+ }, {});
55
+
56
+ return cookies[name];
57
+ }
58
+
59
+ export function createEdgeStoreAstroHandler<TCtx>(config: Config<TCtx>) {
60
+ const { provider = EdgeStoreProvider() } = config;
61
+ const log = new Logger(config.logLevel);
62
+ globalThis._EDGE_STORE_LOGGER = log;
63
+ log.debug('Creating EdgeStore Astro handler');
64
+
65
+ return async (context: APIContext) => {
66
+ try {
67
+ const { request } = context;
68
+ const url = new URL(request.url);
69
+
70
+ if (matchPath(url.pathname, 'health')) {
71
+ return new Response('OK');
72
+ } else if (matchPath(url.pathname, 'init')) {
73
+ let ctx = {} as TCtx;
74
+ try {
75
+ ctx =
76
+ 'createContext' in config
77
+ ? await config.createContext(context)
78
+ : ({} as TCtx);
79
+ } catch (err) {
80
+ throw new EdgeStoreError({
81
+ message: 'Error creating context',
82
+ code: 'CREATE_CONTEXT_ERROR',
83
+ cause: err instanceof Error ? err : undefined,
84
+ });
85
+ }
86
+ const { newCookies, token, baseUrl } = await init({
87
+ ctx,
88
+ provider,
89
+ router: config.router,
90
+ });
91
+
92
+ const headers = new Headers();
93
+ headers.set('Content-Type', 'application/json');
94
+
95
+ // Set cookies
96
+ if (Array.isArray(newCookies)) {
97
+ for (const cookie of newCookies) {
98
+ headers.append('Set-Cookie', cookie);
99
+ }
100
+ } else if (newCookies) {
101
+ headers.append('Set-Cookie', newCookies);
102
+ }
103
+
104
+ return new Response(
105
+ JSON.stringify({
106
+ token,
107
+ baseUrl,
108
+ }),
109
+ { headers },
110
+ );
111
+ } else if (matchPath(url.pathname, 'request-upload')) {
112
+ const body = (await request.json()) as RequestUploadBody;
113
+ const result = await requestUpload({
114
+ provider,
115
+ router: config.router,
116
+ body,
117
+ ctxToken: getCookie(request, 'edgestore-ctx'),
118
+ });
119
+
120
+ return new Response(JSON.stringify(result), {
121
+ headers: { 'Content-Type': 'application/json' },
122
+ });
123
+ } else if (matchPath(url.pathname, 'request-upload-parts')) {
124
+ const body = (await request.json()) as RequestUploadPartsParams;
125
+ const result = await requestUploadParts({
126
+ provider,
127
+ router: config.router,
128
+ body,
129
+ ctxToken: getCookie(request, 'edgestore-ctx'),
130
+ });
131
+
132
+ return new Response(JSON.stringify(result), {
133
+ headers: { 'Content-Type': 'application/json' },
134
+ });
135
+ } else if (matchPath(url.pathname, 'complete-multipart-upload')) {
136
+ const body = (await request.json()) as CompleteMultipartUploadBody;
137
+ await completeMultipartUpload({
138
+ provider,
139
+ router: config.router,
140
+ body,
141
+ ctxToken: getCookie(request, 'edgestore-ctx'),
142
+ });
143
+
144
+ return new Response(null, { status: 200 });
145
+ } else if (matchPath(url.pathname, 'confirm-upload')) {
146
+ const body = (await request.json()) as ConfirmUploadBody;
147
+ const result = await confirmUpload({
148
+ provider,
149
+ router: config.router,
150
+ body,
151
+ ctxToken: getCookie(request, 'edgestore-ctx'),
152
+ });
153
+
154
+ return new Response(JSON.stringify(result), {
155
+ headers: { 'Content-Type': 'application/json' },
156
+ });
157
+ } else if (matchPath(url.pathname, 'delete-file')) {
158
+ const body = (await request.json()) as DeleteFileBody;
159
+ const result = await deleteFile({
160
+ provider,
161
+ router: config.router,
162
+ body,
163
+ ctxToken: getCookie(request, 'edgestore-ctx'),
164
+ });
165
+
166
+ return new Response(JSON.stringify(result), {
167
+ headers: { 'Content-Type': 'application/json' },
168
+ });
169
+ } else if (matchPath(url.pathname, 'proxy-file')) {
170
+ const url = new URL(request.url).searchParams.get('url');
171
+
172
+ if (typeof url === 'string') {
173
+ const cookieHeader = request.headers.get('cookie') ?? '';
174
+
175
+ const proxyRes = await fetch(url, {
176
+ headers: {
177
+ cookie: cookieHeader,
178
+ },
179
+ });
180
+
181
+ const data = await proxyRes.arrayBuffer();
182
+ const headers = new Headers();
183
+ headers.set(
184
+ 'Content-Type',
185
+ proxyRes.headers.get('Content-Type') ?? 'application/octet-stream',
186
+ );
187
+
188
+ return new Response(data, { headers });
189
+ } else {
190
+ return new Response(null, { status: 400 });
191
+ }
192
+ } else {
193
+ return new Response(null, { status: 404 });
194
+ }
195
+ } catch (err) {
196
+ if (err instanceof EdgeStoreError) {
197
+ log[err.level](err.formattedMessage());
198
+ if (err.cause) log[err.level](err.cause);
199
+
200
+ return new Response(JSON.stringify(err.formattedJson()), {
201
+ status: EDGE_STORE_ERROR_CODES[err.code as EdgeStoreErrorCodeKey],
202
+ headers: { 'Content-Type': 'application/json' },
203
+ });
204
+ } else {
205
+ log.error(err);
206
+
207
+ return new Response(
208
+ JSON.stringify(
209
+ new EdgeStoreError({
210
+ message: 'Internal Server Error',
211
+ code: 'SERVER_ERROR',
212
+ }).formattedJson(),
213
+ ),
214
+ {
215
+ status: 500,
216
+ headers: { 'Content-Type': 'application/json' },
217
+ },
218
+ );
219
+ }
220
+ }
221
+ };
222
+ }
@@ -0,0 +1,195 @@
1
+ import {
2
+ EDGE_STORE_ERROR_CODES,
3
+ EdgeStoreError,
4
+ type EdgeStoreErrorCodeKey,
5
+ type EdgeStoreRouter,
6
+ type MaybePromise,
7
+ type Provider,
8
+ } from '@edgestore/shared';
9
+ import { type Context } from 'hono';
10
+ import Logger, { type LogLevel } from '../../libs/logger';
11
+ import { matchPath } from '../../libs/utils';
12
+ import { EdgeStoreProvider } from '../../providers/edgestore';
13
+ import {
14
+ completeMultipartUpload,
15
+ confirmUpload,
16
+ deleteFile,
17
+ init,
18
+ requestUpload,
19
+ requestUploadParts,
20
+ type CompleteMultipartUploadBody,
21
+ type ConfirmUploadBody,
22
+ type DeleteFileBody,
23
+ type RequestUploadBody,
24
+ type RequestUploadPartsParams,
25
+ } from '../shared';
26
+
27
+ export type CreateContextOptions = {
28
+ c: Context;
29
+ };
30
+
31
+ export type Config<TCtx> = {
32
+ provider?: Provider;
33
+ router: EdgeStoreRouter<TCtx>;
34
+ logLevel?: LogLevel;
35
+ } & (TCtx extends Record<string, never>
36
+ ? object
37
+ : {
38
+ provider?: Provider;
39
+ router: EdgeStoreRouter<TCtx>;
40
+ createContext: (opts: CreateContextOptions) => MaybePromise<TCtx>;
41
+ });
42
+
43
+ declare const globalThis: {
44
+ _EDGE_STORE_LOGGER: Logger;
45
+ };
46
+
47
+ // Helper to get a cookie value from Hono Context
48
+ function getCookie(c: Context, name: string): string | undefined {
49
+ const cookies = c.req.header('cookie');
50
+ if (!cookies) return undefined;
51
+
52
+ const match = new RegExp(`${name}=([^;]+)`).exec(cookies);
53
+ return match ? match[1] : undefined;
54
+ }
55
+
56
+ export function createEdgeStoreHonoHandler<TCtx>(config: Config<TCtx>) {
57
+ const { provider = EdgeStoreProvider() } = config;
58
+ const log = new Logger(config.logLevel);
59
+ globalThis._EDGE_STORE_LOGGER = log;
60
+ log.debug('Creating EdgeStore Hono handler');
61
+
62
+ return async (c: Context) => {
63
+ try {
64
+ const pathname = new URL(c.req.url).pathname;
65
+
66
+ if (matchPath(pathname, '/health')) {
67
+ return c.text('OK');
68
+ } else if (matchPath(pathname, '/init')) {
69
+ let ctx = {} as TCtx;
70
+ try {
71
+ ctx =
72
+ 'createContext' in config
73
+ ? await config.createContext({ c })
74
+ : ({} as TCtx);
75
+ } catch (err) {
76
+ throw new EdgeStoreError({
77
+ message: 'Error creating context',
78
+ code: 'CREATE_CONTEXT_ERROR',
79
+ cause: err instanceof Error ? err : undefined,
80
+ });
81
+ }
82
+ const { newCookies, token, baseUrl } = await init({
83
+ ctx,
84
+ provider,
85
+ router: config.router,
86
+ });
87
+
88
+ // Set cookies
89
+ if (Array.isArray(newCookies)) {
90
+ for (const cookie of newCookies) {
91
+ c.header('Set-Cookie', cookie);
92
+ }
93
+ } else if (newCookies) {
94
+ c.header('Set-Cookie', newCookies);
95
+ }
96
+
97
+ return c.json({
98
+ token,
99
+ baseUrl,
100
+ });
101
+ } else if (matchPath(pathname, '/request-upload')) {
102
+ const body = await c.req.json<RequestUploadBody>();
103
+ return c.json(
104
+ await requestUpload({
105
+ provider,
106
+ router: config.router,
107
+ body,
108
+ ctxToken: getCookie(c, 'edgestore-ctx'),
109
+ }),
110
+ );
111
+ } else if (matchPath(pathname, '/request-upload-parts')) {
112
+ const body = await c.req.json<RequestUploadPartsParams>();
113
+ return c.json(
114
+ await requestUploadParts({
115
+ provider,
116
+ router: config.router,
117
+ body,
118
+ ctxToken: getCookie(c, 'edgestore-ctx'),
119
+ }),
120
+ );
121
+ } else if (matchPath(pathname, '/complete-multipart-upload')) {
122
+ const body = await c.req.json<CompleteMultipartUploadBody>();
123
+ await completeMultipartUpload({
124
+ provider,
125
+ router: config.router,
126
+ body,
127
+ ctxToken: getCookie(c, 'edgestore-ctx'),
128
+ });
129
+ return c.body(null, 200);
130
+ } else if (matchPath(pathname, '/confirm-upload')) {
131
+ const body = await c.req.json<ConfirmUploadBody>();
132
+ return c.json(
133
+ await confirmUpload({
134
+ provider,
135
+ router: config.router,
136
+ body,
137
+ ctxToken: getCookie(c, 'edgestore-ctx'),
138
+ }),
139
+ );
140
+ } else if (matchPath(pathname, '/delete-file')) {
141
+ const body = await c.req.json<DeleteFileBody>();
142
+ return c.json(
143
+ await deleteFile({
144
+ provider,
145
+ router: config.router,
146
+ body,
147
+ ctxToken: getCookie(c, 'edgestore-ctx'),
148
+ }),
149
+ );
150
+ } else if (matchPath(pathname, '/proxy-file')) {
151
+ const url = c.req.query('url');
152
+
153
+ if (typeof url === 'string') {
154
+ const cookieHeader = c.req.header('cookie') ?? '';
155
+
156
+ const proxyRes = await fetch(url, {
157
+ headers: {
158
+ cookie: cookieHeader,
159
+ },
160
+ });
161
+
162
+ const data = await proxyRes.arrayBuffer();
163
+ c.header(
164
+ 'Content-Type',
165
+ proxyRes.headers.get('Content-Type') ?? 'application/octet-stream',
166
+ );
167
+
168
+ return c.body(Buffer.from(data));
169
+ } else {
170
+ return c.body(null, 400);
171
+ }
172
+ } else {
173
+ return c.body(null, 404);
174
+ }
175
+ } catch (err) {
176
+ if (err instanceof EdgeStoreError) {
177
+ log[err.level](err.formattedMessage());
178
+ if (err.cause) log[err.level](err.cause);
179
+ return c.json(
180
+ err.formattedJson(),
181
+ EDGE_STORE_ERROR_CODES[err.code as EdgeStoreErrorCodeKey],
182
+ );
183
+ } else {
184
+ log.error(err);
185
+ return c.json(
186
+ new EdgeStoreError({
187
+ message: 'Internal Server Error',
188
+ code: 'SERVER_ERROR',
189
+ }).formattedJson(),
190
+ 500,
191
+ );
192
+ }
193
+ }
194
+ };
195
+ }
@@ -0,0 +1,201 @@
1
+ import {
2
+ EDGE_STORE_ERROR_CODES,
3
+ EdgeStoreError,
4
+ type EdgeStoreErrorCodeKey,
5
+ type EdgeStoreRouter,
6
+ type MaybePromise,
7
+ type Provider,
8
+ } from '@edgestore/shared';
9
+ import Logger, { type LogLevel } from '../../libs/logger';
10
+ import { matchPath } from '../../libs/utils';
11
+ import { EdgeStoreProvider } from '../../providers/edgestore';
12
+ import {
13
+ completeMultipartUpload,
14
+ confirmUpload,
15
+ deleteFile,
16
+ init,
17
+ requestUpload,
18
+ requestUploadParts,
19
+ type CompleteMultipartUploadBody,
20
+ type ConfirmUploadBody,
21
+ type DeleteFileBody,
22
+ type RequestUploadBody,
23
+ type RequestUploadPartsParams,
24
+ } from '../shared';
25
+
26
+ export type CreateContextOptions = {
27
+ req: Request;
28
+ };
29
+
30
+ export type Config<TCtx> = {
31
+ provider?: Provider;
32
+ router: EdgeStoreRouter<TCtx>;
33
+ logLevel?: LogLevel;
34
+ } & (TCtx extends Record<string, never>
35
+ ? object
36
+ : {
37
+ provider?: Provider;
38
+ router: EdgeStoreRouter<TCtx>;
39
+ createContext: (opts: CreateContextOptions) => MaybePromise<TCtx>;
40
+ });
41
+
42
+ declare const globalThis: {
43
+ _EDGE_STORE_LOGGER: Logger;
44
+ };
45
+
46
+ // Helper to safely get cookies from Remix request
47
+ function getCookie(req: Request, name: string): string | undefined {
48
+ const cookieHeader = req.headers.get('cookie');
49
+ if (!cookieHeader) return undefined;
50
+
51
+ const cookies = cookieHeader
52
+ .split(';')
53
+ .reduce<Record<string, string>>((acc, cookie) => {
54
+ const [key, value] = cookie.trim().split('=');
55
+ if (key && value) acc[key] = value;
56
+ return acc;
57
+ }, {});
58
+
59
+ return cookies[name];
60
+ }
61
+
62
+ export function createEdgeStoreRemixHandler<TCtx>(config: Config<TCtx>) {
63
+ const { provider = EdgeStoreProvider() } = config;
64
+ const log = new Logger(config.logLevel);
65
+ globalThis._EDGE_STORE_LOGGER = log;
66
+ log.debug('Creating EdgeStore Remix handler');
67
+
68
+ return async ({ request: req }: { request: Request }) => {
69
+ try {
70
+ const url = new URL(req.url);
71
+ const pathname = url.pathname;
72
+
73
+ if (matchPath(pathname, '/health')) {
74
+ return new Response('OK');
75
+ } else if (matchPath(pathname, '/init')) {
76
+ let ctx = {} as TCtx;
77
+ try {
78
+ ctx =
79
+ 'createContext' in config
80
+ ? await config.createContext({ req })
81
+ : ({} as TCtx);
82
+ } catch (err) {
83
+ throw new EdgeStoreError({
84
+ message: 'Error creating context',
85
+ code: 'CREATE_CONTEXT_ERROR',
86
+ cause: err instanceof Error ? err : undefined,
87
+ });
88
+ }
89
+ const { newCookies, token, baseUrl } = await init({
90
+ ctx,
91
+ provider,
92
+ router: config.router,
93
+ });
94
+
95
+ // Create response with cookies and token
96
+ const responseHeaders = new Headers();
97
+
98
+ if (Array.isArray(newCookies)) {
99
+ for (const cookie of newCookies) {
100
+ responseHeaders.append('Set-Cookie', cookie);
101
+ }
102
+ } else if (newCookies) {
103
+ responseHeaders.append('Set-Cookie', newCookies);
104
+ }
105
+
106
+ return new Response(JSON.stringify({ token, baseUrl }), {
107
+ headers: responseHeaders,
108
+ status: 200,
109
+ });
110
+ } else if (matchPath(pathname, '/request-upload')) {
111
+ const body = (await req.json()) as RequestUploadBody;
112
+ return Response.json(
113
+ await requestUpload({
114
+ provider,
115
+ router: config.router,
116
+ body,
117
+ ctxToken: getCookie(req, 'edgestore-ctx'),
118
+ }),
119
+ );
120
+ } else if (matchPath(pathname, '/request-upload-parts')) {
121
+ const body = (await req.json()) as RequestUploadPartsParams;
122
+ return Response.json(
123
+ await requestUploadParts({
124
+ provider,
125
+ router: config.router,
126
+ body,
127
+ ctxToken: getCookie(req, 'edgestore-ctx'),
128
+ }),
129
+ );
130
+ } else if (matchPath(pathname, '/complete-multipart-upload')) {
131
+ const body = (await req.json()) as CompleteMultipartUploadBody;
132
+ await completeMultipartUpload({
133
+ provider,
134
+ router: config.router,
135
+ body,
136
+ ctxToken: getCookie(req, 'edgestore-ctx'),
137
+ });
138
+ return new Response(null, { status: 200 });
139
+ } else if (matchPath(pathname, '/confirm-upload')) {
140
+ const body = (await req.json()) as ConfirmUploadBody;
141
+ return Response.json(
142
+ await confirmUpload({
143
+ provider,
144
+ router: config.router,
145
+ body,
146
+ ctxToken: getCookie(req, 'edgestore-ctx'),
147
+ }),
148
+ );
149
+ } else if (matchPath(pathname, '/delete-file')) {
150
+ const body = (await req.json()) as DeleteFileBody;
151
+ return Response.json(
152
+ await deleteFile({
153
+ provider,
154
+ router: config.router,
155
+ body,
156
+ ctxToken: getCookie(req, 'edgestore-ctx'),
157
+ }),
158
+ );
159
+ } else if (matchPath(pathname, '/proxy-file')) {
160
+ const url = new URL(req.url).searchParams.get('url');
161
+ if (typeof url === 'string') {
162
+ const proxyRes = await fetch(url, {
163
+ headers: {
164
+ cookie: req.headers.get('cookie') ?? '',
165
+ },
166
+ });
167
+
168
+ const data = await proxyRes.arrayBuffer();
169
+ const headers = new Headers();
170
+ headers.set(
171
+ 'Content-Type',
172
+ proxyRes.headers.get('Content-Type') ?? 'application/octet-stream',
173
+ );
174
+
175
+ return new Response(data, { headers });
176
+ } else {
177
+ return new Response(null, { status: 400 });
178
+ }
179
+ } else {
180
+ return new Response(null, { status: 404 });
181
+ }
182
+ } catch (err) {
183
+ if (err instanceof EdgeStoreError) {
184
+ log[err.level](err.formattedMessage());
185
+ if (err.cause) log[err.level](err.cause);
186
+ return Response.json(err.formattedJson(), {
187
+ status: EDGE_STORE_ERROR_CODES[err.code as EdgeStoreErrorCodeKey],
188
+ });
189
+ } else {
190
+ log.error(err);
191
+ return Response.json(
192
+ new EdgeStoreError({
193
+ message: 'Internal Server Error',
194
+ code: 'SERVER_ERROR',
195
+ }).formattedJson(),
196
+ { status: 500 },
197
+ );
198
+ }
199
+ }
200
+ };
201
+ }