@uploadista/adapters-express 0.0.3

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 (48) hide show
  1. package/.turbo/turbo-build.log +5 -0
  2. package/.turbo/turbo-check.log +5 -0
  3. package/LICENSE +21 -0
  4. package/README.md +456 -0
  5. package/USAGE.md +164 -0
  6. package/dist/adapter-layer.d.ts +22 -0
  7. package/dist/adapter-layer.d.ts.map +1 -0
  8. package/dist/adapter-layer.js +3 -0
  9. package/dist/error-types.d.ts +24 -0
  10. package/dist/error-types.d.ts.map +1 -0
  11. package/dist/error-types.js +65 -0
  12. package/dist/flow-adapter.d.ts +19 -0
  13. package/dist/flow-adapter.d.ts.map +1 -0
  14. package/dist/flow-adapter.js +80 -0
  15. package/dist/flow-http-handlers.d.ts +9 -0
  16. package/dist/flow-http-handlers.d.ts.map +1 -0
  17. package/dist/flow-http-handlers.js +133 -0
  18. package/dist/http-handlers.d.ts +7 -0
  19. package/dist/http-handlers.d.ts.map +1 -0
  20. package/dist/http-handlers.js +78 -0
  21. package/dist/index.d.ts +4 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +2 -0
  24. package/dist/upload-http-handlers.d.ts +9 -0
  25. package/dist/upload-http-handlers.d.ts.map +1 -0
  26. package/dist/upload-http-handlers.js +113 -0
  27. package/dist/uploadista-adapter-layer.d.ts +24 -0
  28. package/dist/uploadista-adapter-layer.d.ts.map +1 -0
  29. package/dist/uploadista-adapter-layer.js +4 -0
  30. package/dist/uploadista-adapter.d.ts +78 -0
  31. package/dist/uploadista-adapter.d.ts.map +1 -0
  32. package/dist/uploadista-adapter.js +297 -0
  33. package/dist/uploadista-websocket-handler.d.ts +9 -0
  34. package/dist/uploadista-websocket-handler.d.ts.map +1 -0
  35. package/dist/uploadista-websocket-handler.js +132 -0
  36. package/dist/websocket-handler.d.ts +8 -0
  37. package/dist/websocket-handler.d.ts.map +1 -0
  38. package/dist/websocket-handler.js +82 -0
  39. package/package.json +40 -0
  40. package/src/error-types.ts +103 -0
  41. package/src/flow-http-handlers.ts +184 -0
  42. package/src/index.ts +14 -0
  43. package/src/upload-http-handlers.ts +186 -0
  44. package/src/uploadista-adapter-layer.ts +32 -0
  45. package/src/uploadista-adapter.ts +626 -0
  46. package/src/uploadista-websocket-handler.ts +209 -0
  47. package/tsconfig.json +11 -0
  48. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,5 @@
1
+
2
+ 
3
+ > @uploadista/adapters-express@0.0.2 build /Users/denislaboureyras/Documents/uploadista/dev/uploadista-workspace/uploadista-sdk/packages/servers/adapters-express
4
+ > tsc -b
5
+
@@ -0,0 +1,5 @@
1
+
2
+ > @uploadista/adapters-express@ check /Users/denislaboureyras/Documents/uploadista/dev/uploadista/packages/uploadista/servers/adapters-express
3
+ > biome check --write ./src
4
+
5
+ Checked 6 files in 9ms. No fixes applied.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 uploadista
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,456 @@
1
+ # @uploadista/adapters-express
2
+
3
+ Uploadista adapter for Express - Run upload servers on Node.js with Express.
4
+
5
+ Provides a complete file upload and flow processing server for Express with manual WebSocket setup, authentication middleware, and support for standard Node.js hosting (Heroku, Railway, VPS, etc.).
6
+
7
+ ## Features
8
+
9
+ - **Node.js Compatible** - Run on any Node.js 18+ environment
10
+ - **Express Middleware** - Integrates with Express request/response patterns
11
+ - **WebSocket Support** - Use `ws` package for real-time progress
12
+ - **Authentication** - Flexible middleware for JWT or custom auth
13
+ - **Multi-Cloud Storage** - S3, Azure, GCS, or filesystem
14
+ - **Redis Support** - Distributed deployments with Redis KV store
15
+ - **TypeScript** - Full type safety with comprehensive JSDoc
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @uploadista/adapters-express express ws
21
+ # or
22
+ pnpm add @uploadista/adapters-express express ws
23
+ ```
24
+
25
+ ## Requirements
26
+
27
+ - Node.js 18+
28
+ - Express 4.0 or 5.0+
29
+ - TypeScript 5.0+ (optional but recommended)
30
+
31
+ ## Quick Start
32
+
33
+ ### 1. Basic Express Server
34
+
35
+ ```typescript
36
+ import express from "express";
37
+ import { createExpressUploadistaAdapter } from "@uploadista/adapters-express";
38
+ import { redisKvStore } from "@uploadista/kv-store-redis";
39
+ import { s3DataStore } from "@uploadista/data-store-s3";
40
+ import { webSocketEventEmitter } from "@uploadista/event-emitter-websocket";
41
+ import { memoryEventBroadcaster } from "@uploadista/event-broadcaster-memory";
42
+
43
+ const app = express();
44
+
45
+ // Create adapter
46
+ const adapter = await createExpressUploadistaAdapter({
47
+ baseUrl: "uploadista",
48
+ dataStore: s3DataStore,
49
+ kvStore: redisKvStore,
50
+ eventEmitter: webSocketEventEmitter,
51
+ eventBroadcaster: memoryEventBroadcaster,
52
+ flows: (flowId, clientId) => createFlowsEffect(flowId, clientId),
53
+ });
54
+
55
+ // Mount HTTP handler
56
+ app.use(`/${adapter.baseUrl}`, (req, res) => {
57
+ adapter.handler(req, res);
58
+ });
59
+
60
+ // WebSocket server
61
+ import http from "http";
62
+ import WebSocket from "ws";
63
+
64
+ const server = http.createServer(app);
65
+ const wss = new WebSocket.Server({ server });
66
+
67
+ wss.on("connection", (ws, req) => {
68
+ adapter.websocketConnectionHandler(ws, req);
69
+ });
70
+
71
+ server.listen(3000, () => {
72
+ console.log("Server running on http://localhost:3000");
73
+ });
74
+ ```
75
+
76
+ ### 2. Add Authentication
77
+
78
+ ```typescript
79
+ const adapter = await createExpressUploadistaAdapter({
80
+ baseUrl: "uploadista",
81
+ dataStore: s3DataStore,
82
+ kvStore: redisKvStore,
83
+ authMiddleware: async (req, res) => {
84
+ const token = req.headers.authorization?.split(" ")[1];
85
+ if (!token) return null;
86
+
87
+ try {
88
+ const payload = await verifyToken(token);
89
+ return {
90
+ clientId: payload.sub,
91
+ permissions: payload.permissions,
92
+ };
93
+ } catch {
94
+ res.status(401).json({ error: "Unauthorized" });
95
+ return null;
96
+ }
97
+ },
98
+ authCacheConfig: { maxSize: 5000, ttl: 3600000 },
99
+ });
100
+ ```
101
+
102
+ ### 3. Express Middleware Setup
103
+
104
+ ```typescript
105
+ import express, { Request, Response, NextFunction } from "express";
106
+
107
+ const app = express();
108
+
109
+ // Body parser for JSON
110
+ app.use(express.json({ limit: "50mb" }));
111
+
112
+ // CORS
113
+ app.use(cors({
114
+ origin: process.env.ALLOWED_ORIGINS?.split(","),
115
+ allowedHeaders: ["Content-Type", "Authorization"],
116
+ }));
117
+
118
+ // Logging
119
+ app.use((req, res, next) => {
120
+ console.log(`${req.method} ${req.path}`);
121
+ next();
122
+ });
123
+
124
+ // Mount adapter
125
+ app.use(`/${adapter.baseUrl}`, (req: Request, res: Response) => {
126
+ adapter.handler(req, res);
127
+ });
128
+
129
+ // Error handler
130
+ app.use((err: any, req: Request, res: Response, next: NextFunction) => {
131
+ console.error(err);
132
+ res.status(500).json({ error: "Internal server error" });
133
+ });
134
+ ```
135
+
136
+ ## Configuration
137
+
138
+ ### `ExpressUploadistaAdapterOptions`
139
+
140
+ ```typescript
141
+ type ExpressUploadistaAdapterOptions = {
142
+ // Required
143
+ flows: (flowId: string, clientId: string | null) =>
144
+ Effect.Effect<unknown, unknown, unknown>;
145
+ dataStore: Layer.Layer<UploadFileDataStores, never, UploadFileKVStore>;
146
+ kvStore: Layer.Layer<BaseKvStoreService>;
147
+
148
+ // Optional
149
+ baseUrl?: string; // Default: "uploadista"
150
+ eventEmitter?: Layer.Layer<BaseEventEmitterService>;
151
+ eventBroadcaster?: Layer.Layer<any>;
152
+ generateId?: Layer.Layer<GenerateId>;
153
+ authMiddleware?: (
154
+ req: IncomingMessage,
155
+ res: ServerResponse,
156
+ ) => Promise<AuthResult>;
157
+ authCacheConfig?: AuthCacheConfig;
158
+ bufferedDataStore?: Layer.Layer<UploadFileDataStore>;
159
+ };
160
+ ```
161
+
162
+ ## API Routes
163
+
164
+ Routes are available at `/{baseUrl}/api/`:
165
+
166
+ ```
167
+ POST /uploadista/api/upload
168
+ Create new upload
169
+
170
+ GET /uploadista/api/upload/:uploadId
171
+ Get upload status
172
+
173
+ PATCH /uploadista/api/upload/:uploadId
174
+ Upload chunk
175
+
176
+ POST /uploadista/api/flow/:flowId/:storageId
177
+ Execute flow
178
+
179
+ GET /uploadista/api/jobs/:jobId/status
180
+ Get job status
181
+
182
+ PATCH /uploadista/api/jobs/:jobId/continue/:nodeId
183
+ Continue flow
184
+ ```
185
+
186
+ ## WebSocket Integration
187
+
188
+ ### Using `ws` Package
189
+
190
+ ```typescript
191
+ import WebSocket from "ws";
192
+ import http from "http";
193
+
194
+ const server = http.createServer(app);
195
+ const wss = new WebSocket.Server({ server });
196
+
197
+ // Handle WebSocket connections
198
+ wss.on("connection", (ws, req) => {
199
+ // Pass to adapter
200
+ adapter.websocketConnectionHandler(ws, req);
201
+
202
+ ws.on("message", (data) => {
203
+ // Adapter handles messages internally
204
+ });
205
+
206
+ ws.on("close", () => {
207
+ console.log("Client disconnected");
208
+ });
209
+ });
210
+
211
+ server.listen(3000);
212
+ ```
213
+
214
+ ### Using Socket.io (Alternative)
215
+
216
+ ```typescript
217
+ import { Server } from "socket.io";
218
+
219
+ const io = new Server(server, {
220
+ cors: { origin: "*" },
221
+ });
222
+
223
+ io.on("connection", (socket) => {
224
+ socket.on("subscribe", (channels) => {
225
+ channels.forEach((ch) => socket.join(ch));
226
+ });
227
+
228
+ socket.on("disconnect", () => {
229
+ console.log("Client disconnected");
230
+ });
231
+ });
232
+
233
+ // Emit events from adapter
234
+ const eventEmitter = /* ... */;
235
+ eventEmitter.on("upload:progress", (data) => {
236
+ io.emit(`upload:${data.uploadId}`, data);
237
+ });
238
+ ```
239
+
240
+ ## Complete Server Example
241
+
242
+ ```typescript
243
+ import express, { Express, Request, Response } from "express";
244
+ import http from "http";
245
+ import WebSocket from "ws";
246
+ import cors from "cors";
247
+ import { createExpressUploadistaAdapter } from "@uploadista/adapters-express";
248
+ import { redisKvStore } from "@uploadista/kv-store-redis";
249
+ import { s3DataStore } from "@uploadista/data-store-s3";
250
+ import { webSocketEventEmitter } from "@uploadista/event-emitter-websocket";
251
+ import { memoryEventBroadcaster } from "@uploadista/event-broadcaster-memory";
252
+ import { verify } from "jsonwebtoken";
253
+
254
+ const app: Express = express();
255
+
256
+ // Middleware
257
+ app.use(express.json({ limit: "50mb" }));
258
+ app.use(cors());
259
+
260
+ // Health check
261
+ app.get("/health", (req: Request, res: Response) => {
262
+ res.json({ status: "ok" });
263
+ });
264
+
265
+ // Create adapter
266
+ const adapter = await createExpressUploadistaAdapter({
267
+ baseUrl: "uploadista",
268
+ dataStore: s3DataStore,
269
+ kvStore: redisKvStore,
270
+ eventEmitter: webSocketEventEmitter,
271
+ eventBroadcaster: memoryEventBroadcaster,
272
+ flows: createFlowsEffect,
273
+ authMiddleware: async (req, res) => {
274
+ const token = req.headers.authorization?.split(" ")[1];
275
+ if (!token) return null;
276
+
277
+ try {
278
+ const payload = verify(token, process.env.JWT_SECRET!);
279
+ return {
280
+ clientId: (payload as any).sub,
281
+ permissions: (payload as any).permissions || [],
282
+ };
283
+ } catch {
284
+ res.statusCode = 401;
285
+ res.end(JSON.stringify({ error: "Unauthorized" }));
286
+ return null;
287
+ }
288
+ },
289
+ authCacheConfig: { maxSize: 5000, ttl: 3600000 },
290
+ });
291
+
292
+ // Mount adapter
293
+ app.use(`/${adapter.baseUrl}`, (req: Request, res: Response) => {
294
+ adapter.handler(req, res);
295
+ });
296
+
297
+ // Error handler
298
+ app.use((err: any, req: Request, res: Response) => {
299
+ console.error(err);
300
+ res.status(500).json({ error: "Internal server error" });
301
+ });
302
+
303
+ // HTTP + WebSocket server
304
+ const server = http.createServer(app);
305
+ const wss = new WebSocket.Server({ server });
306
+
307
+ wss.on("connection", (ws, req) => {
308
+ adapter.websocketConnectionHandler(ws, req);
309
+ });
310
+
311
+ const PORT = process.env.PORT || 3000;
312
+ server.listen(PORT, () => {
313
+ console.log(`Server running on http://localhost:${PORT}`);
314
+ });
315
+
316
+ export default server;
317
+ ```
318
+
319
+ ## Environment Configuration
320
+
321
+ ### .env File
322
+
323
+ ```env
324
+ NODE_ENV=production
325
+ PORT=3000
326
+
327
+ # AWS S3
328
+ AWS_ACCESS_KEY_ID=your-key
329
+ AWS_SECRET_ACCESS_KEY=your-secret
330
+ AWS_REGION=us-east-1
331
+ S3_BUCKET=uploads-prod
332
+
333
+ # Redis (for KV store and events)
334
+ REDIS_URL=redis://localhost:6379
335
+
336
+ # JWT Authentication
337
+ JWT_SECRET=your-jwt-secret
338
+
339
+ # CORS
340
+ ALLOWED_ORIGINS=https://app.example.com,https://dashboard.example.com
341
+ ```
342
+
343
+ ## Docker Deployment
344
+
345
+ ### Dockerfile
346
+
347
+ ```dockerfile
348
+ FROM node:20-alpine
349
+ WORKDIR /app
350
+
351
+ COPY package*.json ./
352
+ RUN npm ci --only=production
353
+
354
+ COPY dist ./dist
355
+
356
+ ENV NODE_ENV=production
357
+ EXPOSE 3000
358
+
359
+ CMD ["node", "dist/server.js"]
360
+ ```
361
+
362
+ ### docker-compose.yml
363
+
364
+ ```yaml
365
+ version: "3.8"
366
+ services:
367
+ app:
368
+ build: .
369
+ ports:
370
+ - "3000:3000"
371
+ environment:
372
+ REDIS_URL: redis://redis:6379
373
+ JWT_SECRET: ${JWT_SECRET}
374
+ AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
375
+ AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
376
+ depends_on:
377
+ - redis
378
+
379
+ redis:
380
+ image: redis:7-alpine
381
+ ports:
382
+ - "6379:6379"
383
+ ```
384
+
385
+ ## Request Examples
386
+
387
+ ### Create Upload
388
+
389
+ ```bash
390
+ curl -X POST http://localhost:3000/uploadista/api/upload \
391
+ -H "Content-Type: application/json" \
392
+ -H "Authorization: Bearer TOKEN" \
393
+ -d '{
394
+ "filename": "document.pdf",
395
+ "size": 5242880,
396
+ "metadata": {"type": "document"}
397
+ }'
398
+ ```
399
+
400
+ ### Upload Chunk
401
+
402
+ ```bash
403
+ curl -X PATCH http://localhost:3000/uploadista/api/upload/upload-123 \
404
+ -H "Content-Range: bytes 0-1048575/5242880" \
405
+ -H "Authorization: Bearer TOKEN" \
406
+ --data-binary @chunk.bin
407
+ ```
408
+
409
+ ## Error Codes
410
+
411
+ - `400 VALIDATION_ERROR` - Invalid request
412
+ - `404 NOT_FOUND` - Upload/flow not found
413
+ - `409 CONFLICT` - Invalid chunk offset
414
+ - `413 PAYLOAD_TOO_LARGE` - File too large
415
+ - `500 INTERNAL_ERROR` - Server error
416
+
417
+ ## Performance Tips
418
+
419
+ 1. Use clustering for multiple CPU cores
420
+ 2. Enable Redis for distributed deployments
421
+ 3. Configure appropriate chunk sizes
422
+ 4. Use reverse proxy (nginx) for load balancing
423
+ 5. Monitor memory and disk usage
424
+
425
+ ## Deployment Options
426
+
427
+ - **VPS**: Deploy with PM2 or systemd
428
+ - **Heroku**: Use Procfile and Redis add-on
429
+ - **Railway**: Direct GitHub integration
430
+ - **Docker**: Container deployment
431
+ - **AWS**: ECS, Lambda (with custom runtime)
432
+ - **DigitalOcean**: App Platform or VPS
433
+
434
+ ## Related Packages
435
+
436
+ - **[@uploadista/server](../server/)** - Core server utilities
437
+ - **[@uploadista/adapters-hono](../adapters-hono/)** - Hono adapter
438
+ - **[@uploadista/adapters-fastify](../adapters-fastify/)** - Fastify adapter
439
+ - **[@uploadista/core](../../core/)** - Core engine
440
+ - **[@uploadista/kv-store-redis](../../kv-stores/redis/)** - Redis KV store
441
+ - **[@uploadista/data-store-s3](../../data-stores/s3/)** - AWS S3 storage
442
+
443
+ ## Troubleshooting
444
+
445
+ ### WebSocket Connection Refused
446
+ Ensure `ws` server is created and `websocketConnectionHandler` is called on connection.
447
+
448
+ ### Memory Leaks
449
+ Check WebSocket connections are properly closed. Use `nodejs --inspect` for profiling.
450
+
451
+ ### Slow Uploads
452
+ Use Redis for distributed deployments. Increase chunk size. Monitor network throughput.
453
+
454
+ ## License
455
+
456
+ MIT
package/USAGE.md ADDED
@@ -0,0 +1,164 @@
1
+ # Express Adapter for Uploadista
2
+
3
+ This package provides an Express.js adapter for the Uploadista upload server, following the same patterns as the Hono adapter but adapted for Express.js applications.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @uploadista/adapters-express express
9
+ # or
10
+ pnpm add @uploadista/adapters-express express
11
+ ```
12
+
13
+ ## Basic Usage
14
+
15
+ ```typescript
16
+ import express from 'express';
17
+ import { createExpressUploadAdapter } from '@uploadista/adapters-express';
18
+ import { Layer } from 'effect';
19
+
20
+ // Set up your layers (same as Hono adapter)
21
+ const userLayer = Layer.mergeAll(
22
+ dataStoreLayer,
23
+ kvStoreLayer,
24
+ eventEmitterLayer,
25
+ generateIdLayer
26
+ );
27
+
28
+ // Create the adapter
29
+ const uploadAdapter = await createExpressUploadAdapter({
30
+ kvStore: kvStoreLayer,
31
+ dataStore: dataStoreLayer,
32
+ eventEmitter: eventEmitterLayer,
33
+ withTracing: true,
34
+ enableWebSockets: true
35
+ });
36
+
37
+ const app = express();
38
+
39
+ // Apply JSON middleware for POST requests
40
+ app.use(express.json());
41
+
42
+ // HTTP endpoints - Express style
43
+ app.use('/api/upload', uploadAdapter.handler);
44
+
45
+ app.listen(3000, () => {
46
+ console.log('Express server running on port 3000');
47
+ });
48
+ ```
49
+
50
+ ## WebSocket Integration
51
+
52
+ Unlike Hono which has built-in WebSocket support, Express requires an external WebSocket library. Here's how to integrate with the popular `ws` library:
53
+
54
+ ```typescript
55
+ import express from 'express';
56
+ import { createServer } from 'http';
57
+ import WebSocket from 'ws';
58
+ import { createExpressUploadAdapter } from '@uploadista/adapters-express';
59
+
60
+ const app = express();
61
+ const server = createServer(app);
62
+
63
+ // Create the upload adapter
64
+ const uploadAdapter = await createExpressUploadAdapter({
65
+ kvStore: kvStoreLayer,
66
+ dataStore: dataStoreLayer,
67
+ eventEmitter: eventEmitterLayer,
68
+ enableWebSockets: true
69
+ });
70
+
71
+ // HTTP endpoints
72
+ app.use('/api/upload', uploadAdapter.handler);
73
+
74
+ // WebSocket server setup
75
+ const wss = new WebSocket.Server({
76
+ server,
77
+ path: '/ws/upload'
78
+ });
79
+
80
+ wss.on('connection', (ws, req) => {
81
+ const connection = {
82
+ id: `conn_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
83
+ send: (data: string) => ws.send(data),
84
+ close: (code?: number, reason?: string) => ws.close(code, reason),
85
+ readyState: ws.readyState
86
+ };
87
+
88
+ // Handle WebSocket connection with Uploadista
89
+ uploadAdapter.websocketHandler(req, connection);
90
+
91
+ // Handle incoming messages
92
+ ws.on('message', (message) => {
93
+ // You can add custom message handling here
94
+ console.log('WebSocket message:', message.toString());
95
+ });
96
+
97
+ // Handle connection close
98
+ ws.on('close', () => {
99
+ console.log('WebSocket connection closed');
100
+ });
101
+ });
102
+
103
+ server.listen(3000, () => {
104
+ console.log('Express server with WebSocket support running on port 3000');
105
+ });
106
+ ```
107
+
108
+ ## Effect-native API
109
+
110
+ For Effect-based applications, you can use the Effect-native API:
111
+
112
+ ```typescript
113
+ import { Effect } from 'effect';
114
+ import { createExpressUploadServer } from '@uploadista/adapters-express';
115
+
116
+ const serverEffect = createExpressUploadServer({
117
+ kvStore: kvStoreLayer,
118
+ dataStore: dataStoreLayer,
119
+ eventEmitter: eventEmitterLayer,
120
+ enableWebSockets: true
121
+ });
122
+
123
+ // Use within Effect context
124
+ const program = Effect.gen(function* () {
125
+ const server = yield* serverEffect;
126
+
127
+ // server.handler, server.upload, and server.websocketHandler
128
+ // are available as Effect-returning functions
129
+ });
130
+ ```
131
+
132
+ ## API Reference
133
+
134
+ ### Types
135
+
136
+ - `ExpressUploadAdapter`: Promise-based adapter for standard Express apps
137
+ - `ExpressUploadServer`: Effect-native server for Effect-based applications
138
+ - `WebSocketConnection`: Interface for WebSocket connection abstraction
139
+ - `ExpressWebSocketHandler`: Type for WebSocket handler functions
140
+
141
+ ### Functions
142
+
143
+ - `createExpressUploadAdapter(options)`: Creates a Promise-based adapter
144
+ - `createExpressUploadServer(options)`: Creates an Effect-native server
145
+ - WebSocket utilities: `createWebSocketHandler`, `createWebSocketMessageHandler`, etc.
146
+
147
+ ## Differences from Hono Adapter
148
+
149
+ 1. **WebSocket Handling**: Express requires external WebSocket library integration
150
+ 2. **Middleware**: Uses Express middleware patterns instead of Hono's context pattern
151
+ 3. **Request/Response**: Works with Express's `req`/`res` objects
152
+ 4. **Error Handling**: Integrates with Express error handling middleware
153
+
154
+ ## Configuration Options
155
+
156
+ Same as the Hono adapter:
157
+
158
+ - `kvStore`: Key-value store layer
159
+ - `dataStore`: Data store layer (depends on kvStore)
160
+ - `eventEmitter`: Event emitter layer
161
+ - `webSocketManager`: Optional WebSocket manager layer
162
+ - `generateId`: Optional ID generation layer
163
+ - `withTracing`: Enable OpenTelemetry tracing
164
+ - `enableWebSockets`: Enable WebSocket support
@@ -0,0 +1,22 @@
1
+ import type { IncomingMessage } from "node:http";
2
+ import type { UploadistaError } from "@uploadista/core/errors";
3
+ import type { InputFile, UploadFile } from "@uploadista/core/types";
4
+ import { type Effect, Context as EffectContext } from "effect";
5
+ import type { Request, Response } from "express";
6
+ export interface WebSocketConnection {
7
+ id: string;
8
+ send: (data: string) => void;
9
+ close: (code?: number, reason?: string) => void;
10
+ readyState: number;
11
+ }
12
+ export type ExpressWebSocketHandler = (req: IncomingMessage, connection: WebSocketConnection) => void;
13
+ export type ExpressAdapterServiceShape = {
14
+ handler: (req: Request, res: Response) => Effect.Effect<void>;
15
+ upload: (file: InputFile, stream: ReadableStream) => Effect.Effect<UploadFile, UploadistaError>;
16
+ websocketHandler: ExpressWebSocketHandler;
17
+ };
18
+ declare const ExpressAdapterService_base: EffectContext.TagClass<ExpressAdapterService, "ExpressAdapterService", ExpressAdapterServiceShape>;
19
+ export declare class ExpressAdapterService extends ExpressAdapterService_base {
20
+ }
21
+ export {};
22
+ //# sourceMappingURL=adapter-layer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-layer.d.ts","sourceRoot":"","sources":["../src/adapter-layer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAE,KAAK,MAAM,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEjD,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,uBAAuB,GAAG,CACpC,GAAG,EAAE,eAAe,EACpB,UAAU,EAAE,mBAAmB,KAC5B,IAAI,CAAC;AAEV,MAAM,MAAM,0BAA0B,GAAG;IACvC,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC9D,MAAM,EAAE,CACN,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,cAAc,KACnB,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;IAChD,gBAAgB,EAAE,uBAAuB,CAAC;CAC3C,CAAC;;AAEF,qBAAa,qBAAsB,SAAQ,0BAEW;CAAG"}
@@ -0,0 +1,3 @@
1
+ import { Context as EffectContext } from "effect";
2
+ export class ExpressAdapterService extends EffectContext.Tag("ExpressAdapterService")() {
3
+ }