@uploadista/server 0.0.8 → 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
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@uploadista/server` will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Unified Adapter Pattern**: Introduced `ServerAdapter<TContext, TResponse, TWebSocket>` interface to standardize framework integration
|
|
13
|
+
- **Core Server Implementation**: Added `createUploadistaServer()` function that handles all common server logic:
|
|
14
|
+
- Route parsing and request type determination
|
|
15
|
+
- Auth middleware execution with 5-second timeout protection
|
|
16
|
+
- Layer composition (upload server, flow server, auth, metrics, plugins)
|
|
17
|
+
- Error handling with `handleFlowError()` utility
|
|
18
|
+
- Effect program execution with optional tracing support
|
|
19
|
+
- **Adapter Interface Types** in `src/adapter/types.ts`:
|
|
20
|
+
- `StandardRequest` - Framework-agnostic request representation
|
|
21
|
+
- `StandardResponse` - Framework-agnostic response representation
|
|
22
|
+
- `WebSocketHandler` - Framework-agnostic WebSocket interface
|
|
23
|
+
- `ServerAdapter` - Interface that framework adapters must implement
|
|
24
|
+
- **Core Server Types** in `src/core/types.ts`:
|
|
25
|
+
- `UploadistaServerConfig` - Configuration for unified server
|
|
26
|
+
- `UploadistaServer` - Return type with handler, websocketHandler, and dispose
|
|
27
|
+
- **Typed Request/Response Routes** in `src/core/routes.ts`:
|
|
28
|
+
- Strongly-typed route definitions for all endpoints (upload, flow, jobs)
|
|
29
|
+
- Request types: `CreateUploadRequest`, `GetUploadRequest`, `UploadChunkRequest`, etc.
|
|
30
|
+
- Response types: `CreateUploadResponse`, `GetUploadResponse`, etc.
|
|
31
|
+
- Error types: `NotFoundRequest`, `BadRequestRequest`, `MethodNotAllowedRequest`
|
|
32
|
+
- **HTTP Handlers** in `src/core/http-handlers/`:
|
|
33
|
+
- `handleUploadistaRequest()` - Central request router
|
|
34
|
+
- Upload handlers: `handleCreateUpload()`, `handleGetUpload()`, `handleUploadChunk()`, `handleGetCapabilities()`
|
|
35
|
+
- Flow handlers: `handleGetFlow()`, `handleRunFlow()`, `handleJobStatus()`, `handleResumeFlow()`, `handlePauseFlow()`, `handleCancelFlow()`
|
|
36
|
+
- **WebSocket Handlers** in `src/core/websocket-handlers/`:
|
|
37
|
+
- `handleWebSocketOpen()` - Subscription management
|
|
38
|
+
- `handleWebSocketMessage()` - Message handling (ping/pong)
|
|
39
|
+
- `handleWebSocketClose()` - Cleanup and unsubscription
|
|
40
|
+
- `handleWebSocketError()` - Error logging
|
|
41
|
+
- **Managed Runtime**: Uses Effect's `ManagedRuntime` for proper resource lifecycle management
|
|
42
|
+
- **Auth Cache Support**: Configurable auth caching via `AuthCacheConfig`
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- **Architecture**: Moved from adapter-specific implementations to centralized core server
|
|
47
|
+
- **Layer Composition**: Now handled uniformly in core server instead of each adapter
|
|
48
|
+
- **Error Handling**: Standardized via `handleFlowError()` across all adapters
|
|
49
|
+
- **Request Routing**: Delegated to adapters via `extractRequest()` returning typed `UploadistaRequest` objects
|
|
50
|
+
|
|
51
|
+
### Benefits
|
|
52
|
+
|
|
53
|
+
- **Code Reduction**: Eliminates ~80% code duplication across framework adapters
|
|
54
|
+
- **Consistency**: All frameworks guaranteed identical routing, auth, and error handling behavior
|
|
55
|
+
- **Maintainability**: Bug fixes and features only need to be implemented once
|
|
56
|
+
- **Testability**: Core logic can be tested independently with mock adapters
|
|
57
|
+
- **Extensibility**: New frameworks can be added with ~100-150 lines of adapter code
|
|
58
|
+
|
|
59
|
+
### Technical Details
|
|
60
|
+
|
|
61
|
+
- Uses Effect.js for composable error handling and layer composition
|
|
62
|
+
- Supports optional OpenTelemetry tracing via `withTracing` flag
|
|
63
|
+
- Integrates with existing observability system (`@uploadista/observability`)
|
|
64
|
+
- Compatible with all event emitters and broadcasters
|
|
65
|
+
- Maintains backward compatibility through separate adapter implementations
|
|
66
|
+
|
|
67
|
+
## [0.0.8] - Previous Version
|
|
68
|
+
|
|
69
|
+
Initial implementation with framework-specific logic in each adapter.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"../auth-BqArZeGK.mjs";export{e as getAuthCredentials};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
const e=async({uploadistaClientId:e,uploadistaApiKey:t,baseUrl:n=`https://api.uploadista.com`})=>{let r=await fetch(`${n}/uploadista/auth/jwt?apiKey=${t}&clientId=${e}`,{method:`GET`,headers:{"Content-Type":`application/json`}});return r.ok===!0?{isValid:!0,data:await r.json()}:{isValid:!1,error:`Failed to get auth credentials`}};export{e as t};
|
|
2
|
-
//# sourceMappingURL=auth-
|
|
2
|
+
//# sourceMappingURL=auth-BqArZeGK.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-BqArZeGK.mjs","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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index--Lny6VJP.d.mts","names":[],"sources":["../src/auth/get-auth-credentials.ts"],"sourcesContent":[],"mappings":";;AAkBA;AAiDA;;;;;;;;;;;;;;;;KAjDY,uBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAiDC;;;;;;;;MAQT,QAAQ"}
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));const c=require(`./auth-B3XCQncE.cjs`);let l=require(`effect`);l=s(l);let u=require(`@uploadista/core/flow`);u=s(u);let d=require(`@uploadista/core/types`);d=s(d);let f=require(`@uploadista/core/upload`);f=s(f);var p=class extends l.Context.Tag(`AuthCacheService`)(){};const m=(e={})=>{let t=e.maxSize??1e4,n=e.ttl??36e5,r=new Map,i=()=>{let e=Date.now();for(let[t,i]of r.entries())e-i.timestamp>n&&r.delete(t)},a=()=>{if(r.size<=t)return;let e=null,n=1/0;for(let[t,i]of r.entries())i.timestamp<n&&(n=i.timestamp,e=t);e&&r.delete(e)};return l.Layer.succeed(p,{set:(e,t)=>l.Effect.sync(()=>{r.size%100==0&&i(),r.set(e,{authContext:t,timestamp:Date.now()}),a()}),get:e=>l.Effect.sync(()=>{let t=r.get(e);return t?Date.now()-t.timestamp>n?(r.delete(e),null):t.authContext:null}),delete:e=>l.Effect.sync(()=>{r.delete(e)}),clear:()=>l.Effect.sync(()=>{r.clear()}),size:()=>l.Effect.sync(()=>r.size)})},h=l.Layer.succeed(p,{set:()=>l.Effect.void,get:()=>l.Effect.succeed(null),delete:()=>l.Effect.void,clear:()=>l.Effect.void,size:()=>l.Effect.succeed(0)});var g=class extends Error{constructor(e,t=500,n=`INTERNAL_ERROR`){super(e),this.statusCode=t,this.errorCode=n,this.name=`AdapterError`}},_=class extends g{constructor(e){super(e,400,`VALIDATION_ERROR`),this.name=`ValidationError`}},v=class extends g{constructor(e){super(`${e} not found`,404,`NOT_FOUND`),this.name=`NotFoundError`}},y=class extends g{constructor(e){super(e,400,`BAD_REQUEST`),this.name=`BadRequestError`}};const b=e=>({error:e.message,code:e.errorCode,timestamp:new Date().toISOString()}),x=e=>{let t={error:e.body,code:e.code,timestamp:new Date().toISOString()};return e.details!==void 0&&(t.details=e.details),t},S=(e=`Internal server error`)=>({error:e,code:`INTERNAL_ERROR`,timestamp:new Date().toISOString()}),C=e=>e.split(`/`).filter(Boolean),w=e=>{let t=C(e);return t[t.length-1]},T=(e,t)=>e.includes(`${t}/api/`),E=(e,t)=>e.replace(`${t}/api/`,``).split(`/`).filter(Boolean),D=e=>{let t=500,n=`UNKNOWN_ERROR`,r=`Internal server error`,i;if(typeof e==`object`&&e){let a=e;if(`code`in a&&typeof a.code==`string`&&(n=a.code),`message`in a&&typeof a.message==`string`?r=a.message:`body`in a&&typeof a.body==`string`&&(r=a.body),`details`in a&&(i=a.details),`status`in a&&typeof a.status==`number`)t=a.status;else if(`code`in a)switch(a.code){case`FILE_NOT_FOUND`:case`FLOW_JOB_NOT_FOUND`:case`UPLOAD_ID_NOT_FOUND`:t=404;break;case`FLOW_JOB_ERROR`:case`VALIDATION_ERROR`:case`INVALID_METADATA`:case`INVALID_LENGTH`:case`ABORTED`:case`INVALID_TERMINATION`:t=400;break;case`INVALID_OFFSET`:t=409;break;case`ERR_SIZE_EXCEEDED`:case`ERR_MAX_SIZE_EXCEEDED`:t=413;break;case`FILE_NO_LONGER_EXISTS`:t=410;break;case`MISSING_OFFSET`:case`INVALID_CONTENT_TYPE`:t=403;break;default:t=500}`message`in a&&a.message===`Invalid JSON body`&&(t=400,n=`VALIDATION_ERROR`)}let a={status:t,code:n,message:r};return i!==void 0&&(a.details=i),a},O=e=>e[e.length-2],k=e=>({jobId:e[e.length-3],nodeId:e[e.length-1]}),A=e=>({storageId:e.pop(),flowId:e.pop()}),j=({kvStore:e,eventEmitter:t,dataStore:n,bufferedDataStore:r,generateId:i})=>{let a=l.Layer.provide(d.uploadFileKvStore,e),o=l.Layer.provide(n,a),s=r?l.Layer.provide(r,a):l.Layer.empty,c=l.Layer.provide(d.uploadEventEmitter,t),u=l.Layer.mergeAll(o,a,c,...i?[i]:[],s);return l.Layer.provide(f.uploadServer,u)},M=({kvStore:e,eventEmitter:t,flowProvider:n,uploadServer:r})=>{let i=l.Layer.provide(d.flowJobKvStore,e),a=l.Layer.provide(d.flowEventEmitter,t),o=l.Layer.mergeAll(n,a,i,r);return l.Layer.provide(u.flowServer,o)};var N=class extends l.Context.Tag(`AuthContextService`)(){};const P=e=>l.Layer.succeed(N,{getClientId:()=>l.Effect.succeed(e?.clientId??null),getMetadata:()=>l.Effect.succeed(e?.metadata??{}),hasPermission:t=>l.Effect.succeed(e?.permissions?.includes(t)??!1),getAuthContext:()=>l.Effect.succeed(e)}),F=P(null);exports.AdapterError=g,exports.AuthCacheService=p,exports.AuthCacheServiceLive=m,exports.AuthContextService=N,exports.AuthContextServiceLive=P,exports.BadRequestError=y,exports.NoAuthCacheServiceLive=h,exports.NoAuthContextServiceLive=F,exports.NotFoundError=v,exports.ValidationError=_,exports.createErrorResponseBody=b,exports.createFlowServerLayer=M,exports.createGenericErrorResponseBody=S,exports.createUploadServerLayer=j,exports.createUploadistaErrorResponseBody=x,exports.extractFlowAndStorageId=A,exports.extractJobAndNodeId=k,exports.extractJobIdFromStatus=O,exports.getAuthCredentials=c.t,exports.getLastSegment=w,exports.getRouteSegments=E,exports.handleFlowError=D,exports.hasBasePath=T,exports.parseUrlSegments=C;
|
|
1
|
+
const e=require(`./auth-B3XCQncE.cjs`);let t=require(`effect`),n=require(`@uploadista/core/flow`),r=require(`@uploadista/core/types`),i=require(`@uploadista/core/utils`),a=require(`@uploadista/event-broadcaster-memory`),o=require(`@uploadista/event-emitter-websocket`),s=require(`@uploadista/observability`),c=require(`@uploadista/core/upload`),l=require(`@uploadista/core/errors`);var u=class extends t.Context.Tag(`AuthCacheService`)(){};const d=(e={})=>{let n=e.maxSize??1e4,r=e.ttl??36e5,i=new Map,a=()=>{let e=Date.now();for(let[t,n]of i.entries())e-n.timestamp>r&&i.delete(t)},o=()=>{if(i.size<=n)return;let e=null,t=1/0;for(let[n,r]of i.entries())r.timestamp<t&&(t=r.timestamp,e=n);e&&i.delete(e)};return t.Layer.succeed(u,{set:(e,n)=>t.Effect.sync(()=>{i.size%100==0&&a(),i.set(e,{authContext:n,timestamp:Date.now()}),o()}),get:e=>t.Effect.sync(()=>{let t=i.get(e);return t?Date.now()-t.timestamp>r?(i.delete(e),null):t.authContext:null}),delete:e=>t.Effect.sync(()=>{i.delete(e)}),clear:()=>t.Effect.sync(()=>{i.clear()}),size:()=>t.Effect.sync(()=>i.size)})},f=t.Layer.succeed(u,{set:()=>t.Effect.void,get:()=>t.Effect.succeed(null),delete:()=>t.Effect.void,clear:()=>t.Effect.void,size:()=>t.Effect.succeed(0)}),p=e=>e.split(`/`).filter(Boolean),m=e=>{let t=p(e);return t[t.length-1]},h=(e,t)=>e.includes(`${t}/api/`),g=(e,t)=>e.replace(`${t}/api/`,``).split(`/`).filter(Boolean),_=e=>{let t=500,n=`UNKNOWN_ERROR`,r=`Internal server error`,i;if(typeof e==`object`&&e){let a=e;if(`code`in a&&typeof a.code==`string`&&(n=a.code),`message`in a&&typeof a.message==`string`?r=a.message:`body`in a&&typeof a.body==`string`&&(r=a.body),`details`in a&&(i=a.details),`status`in a&&typeof a.status==`number`)t=a.status;else if(`code`in a)switch(a.code){case`FILE_NOT_FOUND`:case`FLOW_JOB_NOT_FOUND`:case`UPLOAD_ID_NOT_FOUND`:t=404;break;case`FLOW_JOB_ERROR`:case`VALIDATION_ERROR`:case`INVALID_METADATA`:case`INVALID_LENGTH`:case`ABORTED`:case`INVALID_TERMINATION`:t=400;break;case`INVALID_OFFSET`:t=409;break;case`ERR_SIZE_EXCEEDED`:case`ERR_MAX_SIZE_EXCEEDED`:t=413;break;case`FILE_NO_LONGER_EXISTS`:t=410;break;case`MISSING_OFFSET`:case`INVALID_CONTENT_TYPE`:t=403;break;default:t=500}`message`in a&&a.message===`Invalid JSON body`&&(t=400,n=`VALIDATION_ERROR`)}let a={status:t,code:n,message:r};return i!==void 0&&(a.details=i),a},v=e=>e[e.length-2],y=e=>({jobId:e[e.length-3],nodeId:e[e.length-1]}),b=e=>({storageId:e.pop(),flowId:e.pop()}),x=({kvStore:e,eventEmitter:n,dataStore:i,bufferedDataStore:a,generateId:o})=>{let s=t.Layer.provide(r.uploadFileKvStore,e),l=t.Layer.provide(i,s),u=a?t.Layer.provide(a,s):t.Layer.empty,d=t.Layer.provide(r.uploadEventEmitter,n),f=t.Layer.mergeAll(l,s,d,...o?[o]:[],u);return t.Layer.provide(c.uploadServer,f)},S=({kvStore:e,eventEmitter:i,flowProvider:a,uploadServer:o})=>{let s=t.Layer.provide(r.flowJobKvStore,e),c=t.Layer.provide(r.flowEventEmitter,i),l=t.Layer.mergeAll(a,c,s,o);return t.Layer.provide(n.flowServer,l)};var C=class extends t.Context.Tag(`AuthContextService`)(){};const w=e=>t.Layer.succeed(C,{getClientId:()=>t.Effect.succeed(e?.clientId??null),getMetadata:()=>t.Effect.succeed(e?.metadata??{}),hasPermission:n=>t.Effect.succeed(e?.permissions?.includes(n)??!1),getAuthContext:()=>t.Effect.succeed(e)}),T=w(null),E=({flowId:e})=>t.Effect.gen(function*(){let r=yield*n.FlowServer,i=yield*(yield*C).getClientId();return i&&(yield*t.Effect.logInfo(`[Flow] Getting flow data: ${e}, client: ${i}`)),{status:200,body:yield*r.getFlowData(e,i)}}),D=({flowId:e,storageId:r,inputs:i})=>t.Effect.gen(function*(){let a=yield*n.FlowServer,o=yield*C,s=yield*u,c=yield*o.getClientId();c?(yield*t.Effect.logInfo(`[Flow] Executing flow: ${e}, storage: ${r}, client: ${c}`),yield*t.Effect.logInfo(JSON.stringify(i,null,2))):(yield*t.Effect.logInfo(`[Flow] Executing flow: ${e}, storage: ${r}`),yield*t.Effect.logInfo(`[Flow] Inputs: ${JSON.stringify(i,null,2)}`)),yield*t.Effect.logInfo(`[Flow] Calling flowServer.runFlow...`);let l=yield*a.runFlow({flowId:e,storageId:r,clientId:c,inputs:i}).pipe(t.Effect.tap(()=>t.Effect.logInfo(`[Flow] runFlow completed successfully`)),t.Effect.tapError(e=>t.Effect.logError(`[Flow] runFlow failed with error: ${e}`))),d=yield*o.getAuthContext();return d&&(yield*s.set(l.id,d)),yield*t.Effect.logInfo(`[Flow] Flow started with jobId: ${l.id}`),{status:200,body:l}}),O=({jobId:e})=>t.Effect.gen(function*(){let r=yield*n.FlowServer,i=yield*C,a=yield*u,o=yield*i.getClientId();if(!e)throw Error(`No job id`);o&&(yield*t.Effect.logInfo(`[Flow] Getting job status: ${e}, client: ${o}`));let s=yield*r.getJobStatus(e);return(s.status===`completed`||s.status===`failed`)&&(yield*a.delete(e),o&&(yield*t.Effect.logInfo(`[Flow] Flow ${s.status}, cleared auth cache: ${e}`))),{status:200,body:s}}),k=({jobId:e,nodeId:r,newData:i})=>t.Effect.gen(function*(){let a=yield*n.FlowServer,o=yield*C,s=yield*u,c=yield*o.getClientId();if(c||=(yield*s.get(e))?.clientId??null,c&&(yield*t.Effect.logInfo(`[Flow] Continuing flow: jobId=${e}, nodeId=${r}, client: ${c}`)),i===void 0)throw Error(`Missing newData`);let l=yield*a.resumeFlow({jobId:e,nodeId:r,newData:i,clientId:c});return(l.status===`completed`||l.status===`failed`)&&(yield*s.delete(e),c&&(yield*t.Effect.logInfo(`[Flow] Flow ${l.status}, cleared auth cache: ${e}`))),{status:200,body:l}}),A=({jobId:e})=>t.Effect.gen(function*(){let r=yield*n.FlowServer,i=yield*C,a=yield*u,o=yield*i.getClientId();o||=(yield*a.get(e))?.clientId??null,o&&(yield*t.Effect.logInfo(`[Flow] Pausing flow: jobId=${e}, client: ${o}`));let s=yield*r.pauseFlow(e,o);return o&&(yield*t.Effect.logInfo(`[Flow] Flow paused: ${e}, status: ${s.status}`)),{status:200,body:s}}),j=({jobId:e})=>t.Effect.gen(function*(){let r=yield*n.FlowServer,i=yield*C,a=yield*u;if(!e)throw Error(`No job id`);let o=yield*i.getClientId();o||=(yield*a.get(e))?.clientId??null,o&&(yield*t.Effect.logInfo(`[Flow] Cancelling flow: jobId=${e}, client: ${o}`));let s=yield*r.cancelFlow(e,o);return yield*a.delete(e),o&&(yield*t.Effect.logInfo(`[Flow] Flow cancelled, cleared auth cache: ${e}`)),{status:200,body:s}});var M=class extends Error{constructor(e,t=500,n=`INTERNAL_ERROR`){super(e),this.statusCode=t,this.errorCode=n,this.name=`AdapterError`}},N=class extends M{constructor(e){super(e,400,`VALIDATION_ERROR`),this.name=`ValidationError`}},P=class extends M{constructor(e){super(`${e} not found`,404,`NOT_FOUND`),this.name=`NotFoundError`}},F=class extends M{constructor(e){super(e,400,`BAD_REQUEST`),this.name=`BadRequestError`}};const I=e=>({error:e.message,code:e.errorCode,timestamp:new Date().toISOString()}),L=e=>{let t={error:e.body,code:e.code,timestamp:new Date().toISOString()};return e.details!==void 0&&(t.details=e.details),t},R=(e=`Internal server error`)=>({error:e,code:`INTERNAL_ERROR`,timestamp:new Date().toISOString()}),z=e=>t.Effect.gen(function*(){let n=yield*c.UploadServer,a=yield*C,o=yield*u,s=yield*a.getClientId();s&&(yield*t.Effect.logInfo(`[Upload] Creating upload for client: ${s}`));let l=yield*t.Effect.sync(()=>r.inputFileSchema.safeParse(e.data));if(!l.success)return yield*t.Effect.fail(new N(`Invalid input file schema`));if(l.data.checksumAlgorithm&&!(0,i.isSupportedAlgorithm)(l.data.checksumAlgorithm))return yield*t.Effect.fail(new N(`Unsupported checksum algorithm: ${l.data.checksumAlgorithm}. Supported algorithms: sha256`));let d=yield*n.createUpload(l.data,s),f=yield*a.getAuthContext();return f&&(yield*o.set(d.id,f)),s&&(yield*t.Effect.logInfo(`[Upload] Upload created: ${d.id} for client: ${s}`)),{status:200,body:d}}),B=({storageId:e})=>t.Effect.gen(function*(){let t=yield*c.UploadServer,n=yield*(yield*C).getClientId();return{status:200,body:{storageId:e,capabilities:yield*t.getCapabilities(e,n),timestamp:new Date().toISOString()}}}),V=({uploadId:e})=>t.Effect.gen(function*(){return{status:200,body:yield*(yield*c.UploadServer).getUpload(e)}}),H=e=>t.Effect.gen(function*(){let n=yield*c.UploadServer,r=yield*C,i=yield*u,a=yield*s.MetricsService,{uploadId:o,data:l}=e,d=yield*r.getClientId(),f=yield*r.getMetadata();if(!d){let e=yield*i.get(o);d=e?.clientId??null,f=e?.metadata??{}}d&&(yield*t.Effect.logInfo(`[Upload] Uploading chunk for upload: ${o}, client: ${d}`));let p=yield*n.uploadChunk(o,d,l);return p.size&&p.offset>=p.size&&(yield*i.delete(o),d&&(yield*t.Effect.logInfo(`[Upload] Upload completed, cleared auth cache: ${o}`)),d&&p.size?(yield*t.Effect.logInfo(`[Upload] Recording metrics for org: ${d}, size: ${p.size}`),yield*t.Effect.forkDaemon(a.recordUpload(d,p.size,f))):yield*t.Effect.logWarning(`[Upload] Cannot record metrics - missing organizationId or size`)),d&&(yield*t.Effect.logInfo(`[Upload] Chunk uploaded for upload: ${o}, client: ${d}`)),{status:200,body:p}}),U=e=>t.Effect.gen(function*(){switch(e.type){case`create-upload`:return yield*z(e);case`get-capabilities`:return yield*B(e);case`get-upload`:return yield*V(e);case`upload-chunk`:return yield*H(e);case`get-flow`:return yield*E(e);case`run-flow`:return yield*D(e);case`job-status`:return yield*O(e);case`resume-flow`:return yield*k(e);case`pause-flow`:return yield*A(e);case`cancel-flow`:return yield*j(e);case`not-found`:return{status:404,headers:{"Content-Type":`application/json`},body:{error:`Not found`}};case`bad-request`:return{status:400,body:{error:`Bad request`,message:e.message}};case`method-not-allowed`:return{status:405,headers:{"Content-Type":`application/json`},body:{error:`Method not allowed`}};case`unsupported-content-type`:return{status:415,headers:{"Content-Type":`application/json`},body:{error:`Unsupported content type`}}}}),W=async({flows:e,dataStore:c,kvStore:l,plugins:u=[],eventEmitter:f,eventBroadcaster:p=a.memoryEventBroadcaster,withTracing:m=!1,baseUrl:h=`uploadista`,generateId:g=i.GenerateIdLive,metricsLayer:v,bufferedDataStore:y,adapter:b,authCacheConfig:C})=>{let T=f??(0,o.webSocketEventEmitter)(p),E=h.endsWith(`/`)?h.slice(0,-1):h,D=t.Layer.effect(n.FlowProvider,t.Effect.succeed({getFlow:(t,n)=>e(t,n)}));if(!T)throw Error(`eventEmitter is required. Provide an event emitter layer in the configuration.`);let O=x({kvStore:l,eventEmitter:T,dataStore:await(0,r.createDataStoreLayer)(c),bufferedDataStore:y,generateId:g}),k=S({kvStore:l,eventEmitter:T,flowProvider:D,uploadServer:O}),A=d(C),j=v??s.NoOpMetricsServiceLive,M=t.Layer.mergeAll(O,k,j,A,...u),N=t.ManagedRuntime.make(M);return{handler:async e=>{let r=t.Effect.gen(function*(){let r=yield*b.extractRequest(e,{baseUrl:E}),i=null;if(b.runAuthMiddleware){let n=yield*b.runAuthMiddleware(e).pipe(t.Effect.timeout(`5 seconds`),t.Effect.catchAll(()=>(console.error(`Auth middleware timeout exceeded (5 seconds)`),t.Effect.succeed({_tag:`TimeoutError`}))),t.Effect.catchAllCause(e=>(console.error(`Auth middleware error:`,e),t.Effect.succeed({_tag:`AuthError`,error:e}))));if(n&&typeof n==`object`&&`_tag`in n&&n._tag===`TimeoutError`)return yield*b.sendResponse({status:503,headers:{"Content-Type":`application/json`},body:{error:`Authentication service unavailable`,message:`Authentication took too long to respond. Please try again.`}},e);if(n&&typeof n==`object`&&`_tag`in n&&n._tag===`AuthError`)return yield*b.sendResponse({status:500,headers:{"Content-Type":`application/json`},body:{error:`Internal Server Error`,message:`An error occurred during authentication`}},e);if(n===null)return yield*b.sendResponse({status:401,headers:{"Content-Type":`application/json`},body:{error:`Unauthorized`,message:`Invalid credentials`}},e);i=n}let a=w(i),o=[];if(b.extractWaitUntil){let r=b.extractWaitUntil(e);r&&o.push(t.Layer.succeed(n.FlowWaitUntil,r))}let s=t.Layer.mergeAll(a,A,j,...u,...o);if(r.type===`not-found`)return yield*b.sendResponse({type:`not-found`,status:404,headers:{"Content-Type":`application/json`},body:{error:`Not found`}},e);let c=yield*U(r).pipe(t.Effect.provide(s));return yield*b.sendResponse(c,e)}).pipe(t.Effect.catchAll(t=>{let n=_(t),r={code:n.code,message:n.message};n.details!==void 0&&(r.details=n.details);let i={status:n.status,headers:{"Content-Type":`application/json`},body:r};return b.sendResponse(i,e)}));return m?N.runPromise(r.pipe(t.Effect.provide(s.NodeSdkLive))):N.runPromise(r)},websocketHandler:await N.runPromise(b.webSocketHandler({baseUrl:E})),baseUrl:E,dispose:()=>N.dispose()}},G=(e,n,r)=>t.Effect.gen(function*(){if(!n){yield*t.Effect.sync(()=>{r.send(JSON.stringify({type:`error`,message:`Job ID is required for flow event subscription`,code:`MISSING_JOB_ID`}))});return}yield*e.subscribeToFlowEvents(n,r)}),K=(e,n)=>t.Effect.gen(function*(){n&&(yield*e.unsubscribeFromFlowEvents(n))}),q=(e,n,r)=>t.Effect.gen(function*(){if(!n){yield*t.Effect.sync(()=>{r.send(JSON.stringify({type:`error`,message:`Upload ID is required for upload event subscription`,code:`MISSING_UPLOAD_ID`}))});return}yield*e.subscribeToUploadEvents(n,r)}),J=(e,n)=>t.Effect.gen(function*(){n&&(yield*e.unsubscribeFromUploadEvents(n))}),Y=(e,n,r)=>{let{connection:i,isFlowRoute:a,isUploadRoute:o,jobId:s,uploadId:c,eventId:u}=e;return t.Effect.gen(function*(){a&&(yield*G(r,s,i)),o&&(yield*q(n,c,i)),i.send(JSON.stringify({type:`connection`,message:`Uploadista WebSocket connected`,id:u,jobId:s,uploadId:c,timestamp:new Date().toISOString()}))}).pipe(t.Effect.catchAll(e=>t.Effect.sync(()=>{console.error(`Error subscribing to events:`,e);let t=e instanceof l.UploadistaError?e.body:`Failed to subscribe to events`;i.send(JSON.stringify({type:`error`,message:t,code:e instanceof l.UploadistaError?e.code:`SUBSCRIPTION_ERROR`}))})))},X=(e,n)=>t.Effect.sync(()=>{try{JSON.parse(e).type===`ping`&&n.send(JSON.stringify({type:`pong`,timestamp:new Date().toISOString()}))}catch(e){console.error(`Error handling WebSocket message:`,e),n.send(JSON.stringify({type:`error`,message:`Invalid message format`}))}}),Z=(e,n,r)=>{let{isFlowRoute:i,isUploadRoute:a,jobId:o,uploadId:s}=e;return t.Effect.gen(function*(){i&&(yield*K(r,o)),a&&(yield*J(n,s))}).pipe(t.Effect.catchAll(e=>t.Effect.sync(()=>{console.error(`Error unsubscribing from events:`,e instanceof l.UploadistaError?e.body:e)})))},Q=(e,n)=>t.Effect.sync(()=>{console.error(`WebSocket error for event ${n}:`,e)});exports.AdapterError=M,exports.AuthCacheService=u,exports.AuthCacheServiceLive=d,exports.AuthContextService=C,exports.AuthContextServiceLive=w,exports.BadRequestError=F,exports.NoAuthCacheServiceLive=f,exports.NoAuthContextServiceLive=T,exports.NotFoundError=P,exports.ValidationError=N,exports.createErrorResponseBody=I,exports.createFlowServerLayer=S,exports.createGenericErrorResponseBody=R,exports.createUploadServerLayer=x,exports.createUploadistaErrorResponseBody=L,exports.createUploadistaServer=W,exports.extractFlowAndStorageId=b,exports.extractJobAndNodeId=y,exports.extractJobIdFromStatus=v,exports.getAuthCredentials=e.t,exports.getLastSegment=m,exports.getRouteSegments=g,exports.handleFlowError=_,exports.handleWebSocketClose=Z,exports.handleWebSocketError=Q,exports.handleWebSocketMessage=X,exports.handleWebSocketOpen=Y,exports.hasBasePath=h,exports.parseUrlSegments=p;
|