@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,209 @@
1
+ import type { IncomingMessage } from "node:http";
2
+ import { UploadistaError } from "@uploadista/core/errors";
3
+ import type { FlowServerShape } from "@uploadista/core/flow";
4
+ import type { UploadServerShape } from "@uploadista/core/upload";
5
+ import { Effect } from "effect";
6
+ import type {
7
+ WebSocketConnection,
8
+ WebSocketHandlers,
9
+ } from "./uploadista-adapter-layer";
10
+
11
+ export const createUploadistaWebSocketHandler = (
12
+ baseUrl: string,
13
+ uploadServer: UploadServerShape,
14
+ flowServer: FlowServerShape,
15
+ ) => {
16
+ return (
17
+ req: IncomingMessage,
18
+ connection: WebSocketConnection,
19
+ ): WebSocketHandlers => {
20
+ // Check for ws/uploadista prefix
21
+ const url = req.url || "";
22
+
23
+ if (!url.includes(`${baseUrl}/ws/`)) {
24
+ connection.send(
25
+ JSON.stringify({
26
+ type: "error",
27
+ message: `WebSocket path must start with ${baseUrl}/ws/`,
28
+ }),
29
+ );
30
+ connection.close(1000, "Invalid path");
31
+ // Return no-op handlers since connection is closed
32
+ return {
33
+ onMessage: () => {},
34
+ onClose: () => {},
35
+ onError: () => {},
36
+ };
37
+ }
38
+
39
+ // Remove the prefix and get the actual route segments
40
+ const routeSegments = url
41
+ .replace(`${baseUrl}/ws/`, "")
42
+ .split("/")
43
+ .filter(Boolean);
44
+
45
+ const isUploadRoute = routeSegments.includes("upload");
46
+ const isFlowRoute = routeSegments.includes("flow");
47
+
48
+ // Extract jobId and uploadId from URL path or query parameters
49
+ // Path format: /uploadista/ws/flow/{jobId} or /uploadista/ws/upload/{uploadId}
50
+ let jobId = extractQueryParam(url, "jobId");
51
+ let uploadId = extractQueryParam(url, "uploadId");
52
+
53
+ // If not in query params, extract from path segments
54
+ if (!jobId && !uploadId && routeSegments.length >= 2) {
55
+ const routeType = routeSegments[0]; // 'flow' or 'upload'
56
+ const id = routeSegments[1]; // the actual ID
57
+
58
+ if (routeType === "flow") {
59
+ jobId = id;
60
+ } else if (routeType === "upload") {
61
+ uploadId = id;
62
+ }
63
+ }
64
+
65
+ // Use jobId if available, otherwise use uploadId
66
+ const eventId = jobId || uploadId;
67
+
68
+ console.log("Uploadista websocket handler", { jobId, uploadId, eventId });
69
+
70
+ const subscribeEffect = Effect.gen(function* () {
71
+ // Subscribe to flow events if we had a jobId
72
+ if (isFlowRoute && jobId) {
73
+ // Subscribe to flow events (this handles job tracking)
74
+ yield* flowServer.subscribeToFlowEvents(jobId, connection);
75
+ }
76
+
77
+ // If we have an uploadId, also subscribe to upload events
78
+ // These will be treated as task events within the job
79
+ if (isUploadRoute && uploadId) {
80
+ yield* uploadServer.subscribeToUploadEvents(uploadId, connection);
81
+ }
82
+
83
+ connection.send(
84
+ JSON.stringify({
85
+ type: "connection",
86
+ message: "Uploadista WebSocket connected",
87
+ id: eventId,
88
+ jobId,
89
+ uploadId,
90
+ timestamp: new Date().toISOString(),
91
+ }),
92
+ );
93
+ }).pipe(
94
+ Effect.catchAll((error) =>
95
+ Effect.sync(() => {
96
+ console.error("Error subscribing to events:", error);
97
+ const errorMessage =
98
+ error instanceof UploadistaError
99
+ ? error.body
100
+ : "Failed to subscribe to events";
101
+ connection.send(
102
+ JSON.stringify({
103
+ type: "error",
104
+ message: errorMessage,
105
+ code:
106
+ error instanceof UploadistaError
107
+ ? error.code
108
+ : "SUBSCRIPTION_ERROR",
109
+ }),
110
+ );
111
+ }),
112
+ ),
113
+ );
114
+
115
+ Effect.runFork(subscribeEffect);
116
+
117
+ // Return handlers for WebSocket events
118
+ return {
119
+ onMessage: createWebSocketMessageHandler(
120
+ uploadServer,
121
+ flowServer,
122
+ uploadId,
123
+ jobId,
124
+ connection,
125
+ ),
126
+ onClose: createWebSocketCloseHandler(
127
+ uploadServer,
128
+ flowServer,
129
+ uploadId,
130
+ jobId,
131
+ ),
132
+ onError: createWebSocketErrorHandler(eventId),
133
+ };
134
+ };
135
+ };
136
+
137
+ export const createWebSocketMessageHandler = (
138
+ _uploadServer: UploadServerShape,
139
+ _flowServer: FlowServerShape,
140
+ _uploadId: string | undefined,
141
+ _jobId: string | undefined,
142
+ connection: WebSocketConnection,
143
+ ) => {
144
+ return (message: string): void => {
145
+ try {
146
+ const parsed = JSON.parse(message);
147
+ if (parsed.type === "ping") {
148
+ connection.send(
149
+ JSON.stringify({
150
+ type: "pong",
151
+ timestamp: new Date().toISOString(),
152
+ }),
153
+ );
154
+ }
155
+ } catch (error) {
156
+ console.error("Error handling WebSocket message:", error);
157
+ connection.send(
158
+ JSON.stringify({
159
+ type: "error",
160
+ message: "Invalid message format",
161
+ }),
162
+ );
163
+ }
164
+ };
165
+ };
166
+
167
+ export const createWebSocketCloseHandler = (
168
+ uploadServer: UploadServerShape,
169
+ flowServer: FlowServerShape,
170
+ uploadId: string | undefined,
171
+ jobId: string | undefined,
172
+ ) => {
173
+ return (): void => {
174
+ const unsubscribeEffect = Effect.gen(function* () {
175
+ // Unsubscribe from flow events if we had a jobId
176
+ if (jobId) {
177
+ yield* flowServer.unsubscribeFromFlowEvents(jobId);
178
+ }
179
+
180
+ // Unsubscribe from upload events if we had an uploadId
181
+ if (uploadId) {
182
+ yield* uploadServer.unsubscribeFromUploadEvents(uploadId);
183
+ }
184
+ }).pipe(
185
+ Effect.catchAll((error) =>
186
+ Effect.sync(() => {
187
+ console.error(
188
+ "Error unsubscribing from events:",
189
+ error instanceof UploadistaError ? error.body : error,
190
+ );
191
+ }),
192
+ ),
193
+ );
194
+
195
+ Effect.runFork(unsubscribeEffect);
196
+ };
197
+ };
198
+
199
+ export const createWebSocketErrorHandler = (eventId: string | undefined) => {
200
+ return (error: Error): void => {
201
+ console.error(`WebSocket error for event ${eventId}:`, error);
202
+ };
203
+ };
204
+
205
+ function extractQueryParam(url: string, param: string): string | undefined {
206
+ const regex = new RegExp(`[?&]${param}=([^&]*)`);
207
+ const match = url.match(regex);
208
+ return match?.[1] ? decodeURIComponent(match[1]) : undefined;
209
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "@uploadista/typescript-config/base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src",
6
+ "composite": true,
7
+ "moduleResolution": "bundler",
8
+ "incremental": true
9
+ },
10
+ "include": ["src/**/*"]
11
+ }