@uploadista/server 0.0.7 → 0.0.9
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/CHANGELOG.md +69 -0
- package/dist/auth/index.d.mts +2 -0
- package/dist/auth/index.mjs +1 -0
- package/dist/{auth-C77S4vQd.js → auth-BqArZeGK.mjs} +1 -1
- package/dist/auth-BqArZeGK.mjs.map +1 -0
- package/dist/{index-CvDNB1lJ.d.ts → index--Lny6VJP.d.mts} +1 -1
- package/dist/index--Lny6VJP.d.mts.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +735 -12
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1343 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +10 -7
- package/src/adapter/index.ts +10 -0
- package/src/adapter/types.ts +229 -0
- package/src/core/http-handlers/flow-http-handlers.ts +245 -0
- package/src/core/http-handlers/http-handlers.ts +72 -0
- package/src/core/http-handlers/upload-http-handlers.ts +168 -0
- package/src/core/index.ts +12 -0
- package/src/core/routes.ts +188 -0
- package/src/core/server.ts +327 -0
- package/src/core/types.ts +322 -0
- package/src/core/websocket-handlers/flow-websocket-handlers.ts +47 -0
- package/src/core/websocket-handlers/upload-websocket-handlers.ts +47 -0
- package/src/core/websocket-handlers/websocket-handlers.ts +151 -0
- package/src/core/websocket-routes.ts +136 -0
- package/src/index.ts +2 -0
- package/dist/auth/index.d.ts +0 -2
- package/dist/auth/index.js +0 -1
- package/dist/auth-C77S4vQd.js.map +0 -1
- package/dist/index-CvDNB1lJ.d.ts.map +0 -1
- package/dist/index.d.ts +0 -620
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { UploadServerShape } from "@uploadista/core/upload";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import type { WebSocketConnection } from "../websocket-routes";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Handles subscription to upload events
|
|
7
|
+
* Subscribes the WebSocket connection to receive real-time upload progress events
|
|
8
|
+
*/
|
|
9
|
+
export const handleSubscribeToUploadEvents = (
|
|
10
|
+
uploadServer: UploadServerShape,
|
|
11
|
+
uploadId: string | undefined,
|
|
12
|
+
connection: WebSocketConnection,
|
|
13
|
+
) => {
|
|
14
|
+
return Effect.gen(function* () {
|
|
15
|
+
if (!uploadId) {
|
|
16
|
+
yield* Effect.sync(() => {
|
|
17
|
+
connection.send(
|
|
18
|
+
JSON.stringify({
|
|
19
|
+
type: "error",
|
|
20
|
+
message: "Upload ID is required for upload event subscription",
|
|
21
|
+
code: "MISSING_UPLOAD_ID",
|
|
22
|
+
}),
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
yield* uploadServer.subscribeToUploadEvents(uploadId, connection);
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Handles unsubscription from upload events
|
|
34
|
+
* Removes the WebSocket connection from receiving upload events
|
|
35
|
+
*/
|
|
36
|
+
export const handleUnsubscribeFromUploadEvents = (
|
|
37
|
+
uploadServer: UploadServerShape,
|
|
38
|
+
uploadId: string | undefined,
|
|
39
|
+
) => {
|
|
40
|
+
return Effect.gen(function* () {
|
|
41
|
+
if (!uploadId) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
yield* uploadServer.unsubscribeFromUploadEvents(uploadId);
|
|
46
|
+
});
|
|
47
|
+
};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { UploadistaError } from "@uploadista/core/errors";
|
|
2
|
+
import type { FlowServerShape } from "@uploadista/core/flow";
|
|
3
|
+
import type { UploadServerShape } from "@uploadista/core/upload";
|
|
4
|
+
import { Effect } from "effect";
|
|
5
|
+
import type {
|
|
6
|
+
WebSocketConnection,
|
|
7
|
+
WebSocketConnectionRequest,
|
|
8
|
+
} from "../websocket-routes";
|
|
9
|
+
import {
|
|
10
|
+
handleSubscribeToFlowEvents,
|
|
11
|
+
handleUnsubscribeFromFlowEvents,
|
|
12
|
+
} from "./flow-websocket-handlers";
|
|
13
|
+
import {
|
|
14
|
+
handleSubscribeToUploadEvents,
|
|
15
|
+
handleUnsubscribeFromUploadEvents,
|
|
16
|
+
} from "./upload-websocket-handlers";
|
|
17
|
+
|
|
18
|
+
export type {
|
|
19
|
+
WebSocketConnection,
|
|
20
|
+
WebSocketConnectionRequest,
|
|
21
|
+
} from "../websocket-routes";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Handles WebSocket connection opening
|
|
25
|
+
* Subscribes to the appropriate events based on the connection request
|
|
26
|
+
*/
|
|
27
|
+
export const handleWebSocketOpen = (
|
|
28
|
+
request: WebSocketConnectionRequest,
|
|
29
|
+
uploadServer: UploadServerShape,
|
|
30
|
+
flowServer: FlowServerShape,
|
|
31
|
+
) => {
|
|
32
|
+
const { connection, isFlowRoute, isUploadRoute, jobId, uploadId, eventId } =
|
|
33
|
+
request;
|
|
34
|
+
|
|
35
|
+
return Effect.gen(function* () {
|
|
36
|
+
// Subscribe to flow events if this is a flow route
|
|
37
|
+
if (isFlowRoute) {
|
|
38
|
+
yield* handleSubscribeToFlowEvents(flowServer, jobId, connection);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Subscribe to upload events if this is an upload route
|
|
42
|
+
if (isUploadRoute) {
|
|
43
|
+
yield* handleSubscribeToUploadEvents(uploadServer, uploadId, connection);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Send connection confirmation
|
|
47
|
+
connection.send(
|
|
48
|
+
JSON.stringify({
|
|
49
|
+
type: "connection",
|
|
50
|
+
message: "Uploadista WebSocket connected",
|
|
51
|
+
id: eventId,
|
|
52
|
+
jobId,
|
|
53
|
+
uploadId,
|
|
54
|
+
timestamp: new Date().toISOString(),
|
|
55
|
+
}),
|
|
56
|
+
);
|
|
57
|
+
}).pipe(
|
|
58
|
+
Effect.catchAll((error) =>
|
|
59
|
+
Effect.sync(() => {
|
|
60
|
+
console.error("Error subscribing to events:", error);
|
|
61
|
+
const errorMessage =
|
|
62
|
+
error instanceof UploadistaError
|
|
63
|
+
? error.body
|
|
64
|
+
: "Failed to subscribe to events";
|
|
65
|
+
connection.send(
|
|
66
|
+
JSON.stringify({
|
|
67
|
+
type: "error",
|
|
68
|
+
message: errorMessage,
|
|
69
|
+
code:
|
|
70
|
+
error instanceof UploadistaError
|
|
71
|
+
? error.code
|
|
72
|
+
: "SUBSCRIPTION_ERROR",
|
|
73
|
+
}),
|
|
74
|
+
);
|
|
75
|
+
}),
|
|
76
|
+
),
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Handles incoming WebSocket messages
|
|
82
|
+
* Currently supports ping/pong for connection keep-alive
|
|
83
|
+
*/
|
|
84
|
+
export const handleWebSocketMessage = (
|
|
85
|
+
message: string,
|
|
86
|
+
connection: WebSocketConnection,
|
|
87
|
+
) => {
|
|
88
|
+
return Effect.sync(() => {
|
|
89
|
+
try {
|
|
90
|
+
const parsed = JSON.parse(message);
|
|
91
|
+
if (parsed.type === "ping") {
|
|
92
|
+
connection.send(
|
|
93
|
+
JSON.stringify({
|
|
94
|
+
type: "pong",
|
|
95
|
+
timestamp: new Date().toISOString(),
|
|
96
|
+
}),
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.error("Error handling WebSocket message:", error);
|
|
101
|
+
connection.send(
|
|
102
|
+
JSON.stringify({
|
|
103
|
+
type: "error",
|
|
104
|
+
message: "Invalid message format",
|
|
105
|
+
}),
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Handles WebSocket connection closing
|
|
113
|
+
* Unsubscribes from all events and cleans up resources
|
|
114
|
+
*/
|
|
115
|
+
export const handleWebSocketClose = (
|
|
116
|
+
request: WebSocketConnectionRequest,
|
|
117
|
+
uploadServer: UploadServerShape,
|
|
118
|
+
flowServer: FlowServerShape,
|
|
119
|
+
) => {
|
|
120
|
+
const { isFlowRoute, isUploadRoute, jobId, uploadId } = request;
|
|
121
|
+
|
|
122
|
+
return Effect.gen(function* () {
|
|
123
|
+
// Unsubscribe from flow events if this was a flow route
|
|
124
|
+
if (isFlowRoute) {
|
|
125
|
+
yield* handleUnsubscribeFromFlowEvents(flowServer, jobId);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Unsubscribe from upload events if this was an upload route
|
|
129
|
+
if (isUploadRoute) {
|
|
130
|
+
yield* handleUnsubscribeFromUploadEvents(uploadServer, uploadId);
|
|
131
|
+
}
|
|
132
|
+
}).pipe(
|
|
133
|
+
Effect.catchAll((error) =>
|
|
134
|
+
Effect.sync(() => {
|
|
135
|
+
console.error(
|
|
136
|
+
"Error unsubscribing from events:",
|
|
137
|
+
error instanceof UploadistaError ? error.body : error,
|
|
138
|
+
);
|
|
139
|
+
}),
|
|
140
|
+
),
|
|
141
|
+
);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Handles WebSocket errors
|
|
146
|
+
*/
|
|
147
|
+
export const handleWebSocketError = (error: unknown, eventId?: string) => {
|
|
148
|
+
return Effect.sync(() => {
|
|
149
|
+
console.error(`WebSocket error for event ${eventId}:`, error);
|
|
150
|
+
});
|
|
151
|
+
};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { UploadEvent } from "@uploadista/core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Framework-agnostic WebSocket connection interface
|
|
5
|
+
* Adapters should wrap their native WebSocket implementations to match this interface
|
|
6
|
+
*/
|
|
7
|
+
export type WebSocketConnection = {
|
|
8
|
+
id: string;
|
|
9
|
+
send: (data: string) => void;
|
|
10
|
+
close: (code?: number, reason?: string) => void;
|
|
11
|
+
readyState: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* WebSocket event types for real-time communication
|
|
16
|
+
*/
|
|
17
|
+
export type WebSocketEventType =
|
|
18
|
+
| "subscribe-upload"
|
|
19
|
+
| "subscribe-flow"
|
|
20
|
+
| "unsubscribe-upload"
|
|
21
|
+
| "unsubscribe-flow"
|
|
22
|
+
| "ping"
|
|
23
|
+
| "connection"
|
|
24
|
+
| "upload-event"
|
|
25
|
+
| "flow-event"
|
|
26
|
+
| "error"
|
|
27
|
+
| "invalid-path"
|
|
28
|
+
| "auth-failed";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Base WebSocket event structure
|
|
32
|
+
*/
|
|
33
|
+
export type WebSocketEvent<T extends WebSocketEventType> = {
|
|
34
|
+
type: T;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Incoming WebSocket messages (client -> server)
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
export type SubscribeUploadEvent = WebSocketEvent<"subscribe-upload"> & {
|
|
42
|
+
uploadId: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type SubscribeFlowEvent = WebSocketEvent<"subscribe-flow"> & {
|
|
46
|
+
jobId: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type UnsubscribeUploadEvent = WebSocketEvent<"unsubscribe-upload"> & {
|
|
50
|
+
uploadId: string;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type UnsubscribeFlowEvent = WebSocketEvent<"unsubscribe-flow"> & {
|
|
54
|
+
jobId: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type PingEvent = WebSocketEvent<"ping">;
|
|
58
|
+
|
|
59
|
+
export type WebSocketIncomingEvent =
|
|
60
|
+
| SubscribeUploadEvent
|
|
61
|
+
| SubscribeFlowEvent
|
|
62
|
+
| UnsubscribeUploadEvent
|
|
63
|
+
| UnsubscribeFlowEvent
|
|
64
|
+
| PingEvent;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Outgoing WebSocket messages (server -> client)
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
export type ConnectionEvent = WebSocketEvent<"connection"> & {
|
|
71
|
+
message: string;
|
|
72
|
+
id?: string;
|
|
73
|
+
jobId?: string;
|
|
74
|
+
uploadId?: string;
|
|
75
|
+
timestamp: string;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export type UploadEventMessage = WebSocketEvent<"upload-event"> & {
|
|
79
|
+
uploadId: string;
|
|
80
|
+
event: UploadEvent;
|
|
81
|
+
timestamp: string;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export type FlowEventMessage = WebSocketEvent<"flow-event"> & {
|
|
85
|
+
jobId: string;
|
|
86
|
+
event: unknown;
|
|
87
|
+
timestamp: string;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export type ErrorEvent = WebSocketEvent<"error"> & {
|
|
91
|
+
message: string;
|
|
92
|
+
code?: string;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export type InvalidPathEvent = WebSocketEvent<"invalid-path"> & {
|
|
96
|
+
message: string;
|
|
97
|
+
expectedPrefix: string;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export type AuthFailedEvent = WebSocketEvent<"auth-failed"> & {
|
|
101
|
+
message: string;
|
|
102
|
+
authMethod: "token" | "cookies";
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export type WebSocketOutgoingEvent =
|
|
106
|
+
| ConnectionEvent
|
|
107
|
+
| UploadEventMessage
|
|
108
|
+
| FlowEventMessage
|
|
109
|
+
| ErrorEvent
|
|
110
|
+
| InvalidPathEvent
|
|
111
|
+
| AuthFailedEvent;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* WebSocket connection request with routing information
|
|
115
|
+
* Extracted from the WebSocket URL and query parameters
|
|
116
|
+
*/
|
|
117
|
+
export type WebSocketConnectionRequest = {
|
|
118
|
+
/** Base URL prefix for WebSocket connections */
|
|
119
|
+
baseUrl: string;
|
|
120
|
+
/** Full pathname from the URL */
|
|
121
|
+
pathname: string;
|
|
122
|
+
/** Parsed route segments after baseUrl/ws/ */
|
|
123
|
+
routeSegments: string[];
|
|
124
|
+
/** Whether this is an upload route */
|
|
125
|
+
isUploadRoute: boolean;
|
|
126
|
+
/** Whether this is a flow route */
|
|
127
|
+
isFlowRoute: boolean;
|
|
128
|
+
/** Job ID (for flows) */
|
|
129
|
+
jobId?: string;
|
|
130
|
+
/** Upload ID (for uploads) */
|
|
131
|
+
uploadId?: string;
|
|
132
|
+
/** Event ID (jobId or uploadId) */
|
|
133
|
+
eventId?: string;
|
|
134
|
+
/** WebSocket connection instance */
|
|
135
|
+
connection: WebSocketConnection;
|
|
136
|
+
};
|
package/src/index.ts
CHANGED
package/dist/auth/index.d.ts
DELETED
package/dist/auth/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as e}from"../auth-C77S4vQd.js";export{e as getAuthCredentials};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth-C77S4vQd.js","names":[],"sources":["../src/auth/get-auth-credentials.ts"],"sourcesContent":["/**\n * Response type for authentication credential requests.\n * - `isValid: true` with token and expiration\n * - `isValid: false` with error message\n *\n * @example\n * ```typescript\n * const response = await getAuthCredentials({\n * uploadistaClientId: \"my-client\",\n * uploadistaApiKey: \"sk_...\"\n * });\n * if (response.isValid) {\n * console.log(`Token: ${response.data.token}`);\n * } else {\n * console.error(`Auth failed: ${response.error}`);\n * }\n * ```\n */\nexport type AuthCredentialsResponse =\n | {\n isValid: true;\n data: { token: string; expiresIn: number };\n }\n | {\n isValid: false;\n error: string;\n };\n\n/**\n * Retrieve JWT authentication credentials from the Uploadista server.\n * This function exchanges client credentials (ID + API key) for a signed JWT token.\n *\n * The JWT token is then used in subsequent API requests via the Authorization header.\n * Tokens are time-limited and should be refreshed before expiration.\n *\n * @param params - Credential exchange parameters\n * @param params.uploadistaClientId - Your Uploadista client ID\n * @param params.uploadistaApiKey - Your Uploadista API key (secret)\n * @param params.baseUrl - Uploadista server base URL (default: https://api.uploadista.com)\n * @returns Promise resolving to authentication response with token or error\n *\n * @example\n * ```typescript\n * import { getAuthCredentials } from \"@uploadista/server\";\n *\n * // Get JWT token for API requests\n * const response = await getAuthCredentials({\n * uploadistaClientId: process.env.UPLOADISTA_CLIENT_ID,\n * uploadistaApiKey: process.env.UPLOADISTA_API_KEY,\n * });\n *\n * if (response.isValid) {\n * // Use token in API requests\n * const headers = {\n * Authorization: `Bearer ${response.data.token}`,\n * };\n *\n * // Token expires in response.data.expiresIn seconds\n * setTimeout(\n * () => {\n * // Refresh token before expiration\n * },\n * response.data.expiresIn * 1000,\n * );\n * }\n * ```\n */\nexport const getAuthCredentials = async ({\n uploadistaClientId,\n uploadistaApiKey,\n baseUrl = \"https://api.uploadista.com\",\n}: {\n uploadistaClientId: string;\n uploadistaApiKey: string;\n baseUrl?: string;\n}): Promise<AuthCredentialsResponse> => {\n const response = await fetch(\n `${baseUrl}/uploadista/auth/jwt?apiKey=${uploadistaApiKey}&clientId=${uploadistaClientId}`,\n {\n method: \"GET\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n },\n );\n\n if (response.ok !== true) {\n return { isValid: false, error: \"Failed to get auth credentials\" };\n }\n\n const data = (await response.json()) as { token: string; expiresIn: number };\n\n return {\n isValid: true,\n data,\n };\n};\n"],"mappings":"AAmEA,MAAa,EAAqB,MAAO,CACvC,qBACA,mBACA,UAAU,gCAK4B,CACtC,IAAM,EAAW,MAAM,MACrB,GAAG,EAAQ,8BAA8B,EAAiB,YAAY,IACtE,CACE,OAAQ,MACR,QAAS,CACP,eAAgB,mBACjB,CACF,CACF,CAQD,OANI,EAAS,KAAO,GAMb,CACL,QAAS,GACT,KAJY,MAAM,EAAS,MAAM,CAKlC,CARQ,CAAE,QAAS,GAAO,MAAO,iCAAkC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-CvDNB1lJ.d.ts","names":[],"sources":["../src/auth/get-auth-credentials.ts"],"sourcesContent":[],"mappings":";;AAkBA;AAiDA;;;;;;;;;;;;;;;;KAjDY,uBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAiDC;;;;;;;;MAQT,QAAQ"}
|