@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.
Files changed (37) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/dist/auth/index.d.mts +2 -0
  3. package/dist/auth/index.mjs +1 -0
  4. package/dist/{auth-C77S4vQd.js → auth-BqArZeGK.mjs} +1 -1
  5. package/dist/auth-BqArZeGK.mjs.map +1 -0
  6. package/dist/{index-CvDNB1lJ.d.ts → index--Lny6VJP.d.mts} +1 -1
  7. package/dist/index--Lny6VJP.d.mts.map +1 -0
  8. package/dist/index.cjs +1 -1
  9. package/dist/index.d.cts +735 -12
  10. package/dist/index.d.cts.map +1 -1
  11. package/dist/index.d.mts +1343 -0
  12. package/dist/index.d.mts.map +1 -0
  13. package/dist/index.mjs +2 -0
  14. package/dist/index.mjs.map +1 -0
  15. package/package.json +10 -7
  16. package/src/adapter/index.ts +10 -0
  17. package/src/adapter/types.ts +229 -0
  18. package/src/core/http-handlers/flow-http-handlers.ts +245 -0
  19. package/src/core/http-handlers/http-handlers.ts +72 -0
  20. package/src/core/http-handlers/upload-http-handlers.ts +168 -0
  21. package/src/core/index.ts +12 -0
  22. package/src/core/routes.ts +188 -0
  23. package/src/core/server.ts +327 -0
  24. package/src/core/types.ts +322 -0
  25. package/src/core/websocket-handlers/flow-websocket-handlers.ts +47 -0
  26. package/src/core/websocket-handlers/upload-websocket-handlers.ts +47 -0
  27. package/src/core/websocket-handlers/websocket-handlers.ts +151 -0
  28. package/src/core/websocket-routes.ts +136 -0
  29. package/src/index.ts +2 -0
  30. package/dist/auth/index.d.ts +0 -2
  31. package/dist/auth/index.js +0 -1
  32. package/dist/auth-C77S4vQd.js.map +0 -1
  33. package/dist/index-CvDNB1lJ.d.ts.map +0 -1
  34. package/dist/index.d.ts +0 -620
  35. package/dist/index.d.ts.map +0 -1
  36. package/dist/index.js +0 -2
  37. 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
@@ -1,5 +1,7 @@
1
+ export * from "./adapter";
1
2
  export * from "./auth";
2
3
  export * from "./cache";
4
+ export * from "./core";
3
5
  export * from "./error-types";
4
6
  export * from "./http-utils";
5
7
  export * from "./layer-utils";
@@ -1,2 +0,0 @@
1
- import { n as getAuthCredentials, t as AuthCredentialsResponse } from "../index-CvDNB1lJ.js";
2
- export { AuthCredentialsResponse, getAuthCredentials };
@@ -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"}