@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.
- package/.turbo/turbo-build.log +5 -0
- package/.turbo/turbo-check.log +5 -0
- package/LICENSE +21 -0
- package/README.md +456 -0
- package/USAGE.md +164 -0
- package/dist/adapter-layer.d.ts +22 -0
- package/dist/adapter-layer.d.ts.map +1 -0
- package/dist/adapter-layer.js +3 -0
- package/dist/error-types.d.ts +24 -0
- package/dist/error-types.d.ts.map +1 -0
- package/dist/error-types.js +65 -0
- package/dist/flow-adapter.d.ts +19 -0
- package/dist/flow-adapter.d.ts.map +1 -0
- package/dist/flow-adapter.js +80 -0
- package/dist/flow-http-handlers.d.ts +9 -0
- package/dist/flow-http-handlers.d.ts.map +1 -0
- package/dist/flow-http-handlers.js +133 -0
- package/dist/http-handlers.d.ts +7 -0
- package/dist/http-handlers.d.ts.map +1 -0
- package/dist/http-handlers.js +78 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/upload-http-handlers.d.ts +9 -0
- package/dist/upload-http-handlers.d.ts.map +1 -0
- package/dist/upload-http-handlers.js +113 -0
- package/dist/uploadista-adapter-layer.d.ts +24 -0
- package/dist/uploadista-adapter-layer.d.ts.map +1 -0
- package/dist/uploadista-adapter-layer.js +4 -0
- package/dist/uploadista-adapter.d.ts +78 -0
- package/dist/uploadista-adapter.d.ts.map +1 -0
- package/dist/uploadista-adapter.js +297 -0
- package/dist/uploadista-websocket-handler.d.ts +9 -0
- package/dist/uploadista-websocket-handler.d.ts.map +1 -0
- package/dist/uploadista-websocket-handler.js +132 -0
- package/dist/websocket-handler.d.ts +8 -0
- package/dist/websocket-handler.d.ts.map +1 -0
- package/dist/websocket-handler.js +82 -0
- package/package.json +40 -0
- package/src/error-types.ts +103 -0
- package/src/flow-http-handlers.ts +184 -0
- package/src/index.ts +14 -0
- package/src/upload-http-handlers.ts +186 -0
- package/src/uploadista-adapter-layer.ts +32 -0
- package/src/uploadista-adapter.ts +626 -0
- package/src/uploadista-websocket-handler.ts +209 -0
- package/tsconfig.json +11 -0
- 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