@uploadista/server 0.0.10 → 0.0.12

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.
@@ -0,0 +1,468 @@
1
+ # Type-Safe Plugin System Examples
2
+
3
+ This document demonstrates how to use the improved type-safe plugin system for the Uploadista server.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Basic Concepts](#basic-concepts)
8
+ - [Simple Flow (No Plugins)](#simple-flow-no-plugins)
9
+ - [Flow with Single Plugin](#flow-with-single-plugin)
10
+ - [Flow with Multiple Plugins](#flow-with-multiple-plugins)
11
+ - [Compile-Time Validation](#compile-time-validation)
12
+ - [Mixed Flows (Some with Plugins, Some without)](#mixed-flows-some-with-plugins-some-without)
13
+ - [Migration from Untyped to Typed](#migration-from-untyped-to-typed)
14
+
15
+ ## Basic Concepts
16
+
17
+ ### Plugin Types
18
+
19
+ Each plugin has three related types:
20
+
21
+ 1. **Service Tag** - The Context.Tag class (e.g., `ImagePlugin`)
22
+ 2. **Service Shape** - The interface defining methods (e.g., `ImagePluginShape`)
23
+ 3. **Layer Type** - The Effect Layer type (e.g., `ImagePluginLayer`)
24
+
25
+ ```typescript
26
+ // Defined in @uploadista/core/flow
27
+ export class ImagePlugin extends Context.Tag("ImagePlugin")<
28
+ ImagePlugin,
29
+ ImagePluginShape
30
+ >() {}
31
+
32
+ export type ImagePluginLayer = Layer.Layer<ImagePlugin, never, never>;
33
+ ```
34
+
35
+ ### Type-Safe Flow Function
36
+
37
+ Use `TypeSafeFlowFunction<TRequirements>` to declare plugin dependencies:
38
+
39
+ ```typescript
40
+ import type { TypeSafeFlowFunction } from "@uploadista/server";
41
+ import { ImagePlugin } from "@uploadista/core/flow";
42
+
43
+ // Flow that requires ImagePlugin
44
+ const myFlow: TypeSafeFlowFunction<ImagePlugin> = (flowId, clientId) =>
45
+ Effect.gen(function* () {
46
+ const imageService = yield* ImagePlugin;
47
+ // ...
48
+ });
49
+ ```
50
+
51
+ ## Simple Flow (No Plugins)
52
+
53
+ For flows that don't need any plugins:
54
+
55
+ ```typescript
56
+ import { createTypeSafeServer, defineSimpleFlow } from "@uploadista/server";
57
+ import { createFlow, createInputNode } from "@uploadista/core/flow";
58
+ import { z } from "zod";
59
+
60
+ // Define a simple flow with no plugin dependencies
61
+ const simpleUploadFlow = defineSimpleFlow((flowId, clientId) =>
62
+ Effect.gen(function* () {
63
+ const inputNode = yield* createInputNode("input");
64
+
65
+ return createFlow({
66
+ id: "simple-upload",
67
+ name: "Simple Upload",
68
+ nodes: [inputNode],
69
+ edges: [],
70
+ inputSchema: z.object({
71
+ input: z.object({
72
+ file: z.instanceof(File),
73
+ }),
74
+ }),
75
+ outputSchema: z.object({
76
+ fileUrl: z.string(),
77
+ }),
78
+ });
79
+ }),
80
+ );
81
+
82
+ // Create server with no plugins
83
+ const server = await createTypeSafeServer({
84
+ plugins: [] as const, // No plugins needed
85
+ flows: simpleUploadFlow,
86
+ adapter: honoAdapter({ /* ... */ }),
87
+ dataStore: { type: "s3", config: { bucket: "uploads" } },
88
+ kvStore: redisKvStore,
89
+ });
90
+ ```
91
+
92
+ ## Flow with Single Plugin
93
+
94
+ ### Example: Image Processing Flow
95
+
96
+ ```typescript
97
+ import { createTypeSafeServer, defineFlow } from "@uploadista/server";
98
+ import { ImagePlugin, createFlow, createInputNode } from "@uploadista/core/flow";
99
+ import { createResizeNode } from "@uploadista/flow-images-nodes";
100
+ import { sharpImagePlugin } from "@uploadista/flow-images-sharp";
101
+ import { z } from "zod";
102
+
103
+ // Define flow that requires ImagePlugin
104
+ const imageResizeFlow = defineFlow<ImagePlugin>((flowId, clientId) =>
105
+ Effect.gen(function* () {
106
+ const inputNode = yield* createInputNode("input");
107
+ const resizeNode = yield* createResizeNode("resize", {
108
+ width: 800,
109
+ height: 600,
110
+ fit: "cover",
111
+ });
112
+
113
+ return createFlow({
114
+ id: "image-resize",
115
+ name: "Image Resize",
116
+ nodes: [inputNode, resizeNode],
117
+ edges: [
118
+ { source: "input", target: "resize" },
119
+ ],
120
+ inputSchema: z.object({
121
+ input: z.object({
122
+ file: z.instanceof(File),
123
+ }),
124
+ }),
125
+ outputSchema: z.object({
126
+ resizedUrl: z.string(),
127
+ }),
128
+ });
129
+ }),
130
+ );
131
+
132
+ // Create server with ImagePlugin
133
+ const server = await createTypeSafeServer({
134
+ plugins: [sharpImagePlugin] as const, // ✅ Provides ImagePlugin
135
+ flows: imageResizeFlow,
136
+ adapter: honoAdapter({ /* ... */ }),
137
+ dataStore: { type: "s3", config: { bucket: "uploads" } },
138
+ kvStore: redisKvStore,
139
+ });
140
+ ```
141
+
142
+ ## Flow with Multiple Plugins
143
+
144
+ ### Example: Image Processing + ZIP Archive
145
+
146
+ ```typescript
147
+ import {
148
+ createTypeSafeServer,
149
+ defineFlow,
150
+ type TypeSafeFlowFunction,
151
+ } from "@uploadista/server";
152
+ import {
153
+ ImagePlugin,
154
+ ZipPlugin,
155
+ createFlow,
156
+ createInputNode,
157
+ } from "@uploadista/core/flow";
158
+ import { createResizeNode, createOptimizeNode } from "@uploadista/flow-images-nodes";
159
+ import { createZipNode } from "@uploadista/flow-zip-nodes";
160
+ import { sharpImagePlugin } from "@uploadista/flow-images-sharp";
161
+ import { jsZipPlugin } from "@uploadista/flow-zip-jszip";
162
+ import { z } from "zod";
163
+
164
+ // Flow requires both ImagePlugin AND ZipPlugin
165
+ const imageArchiveFlow: TypeSafeFlowFunction<ImagePlugin | ZipPlugin> = (
166
+ flowId,
167
+ clientId,
168
+ ) =>
169
+ Effect.gen(function* () {
170
+ // Both plugins are available
171
+ const imageService = yield* ImagePlugin;
172
+ const zipService = yield* ZipPlugin;
173
+
174
+ const inputNode = yield* createInputNode("input");
175
+ const resizeNode = yield* createResizeNode("resize", {
176
+ width: 1920,
177
+ height: 1080,
178
+ fit: "cover",
179
+ });
180
+ const optimizeNode = yield* createOptimizeNode("optimize", {
181
+ quality: 85,
182
+ format: "webp",
183
+ });
184
+ const zipNode = yield* createZipNode("zip", {
185
+ zipName: "images.zip",
186
+ includeMetadata: true,
187
+ });
188
+
189
+ return createFlow({
190
+ id: "image-archive",
191
+ name: "Image Processing and Archive",
192
+ nodes: [inputNode, resizeNode, optimizeNode, zipNode],
193
+ edges: [
194
+ { source: "input", target: "resize" },
195
+ { source: "resize", target: "optimize" },
196
+ { source: "optimize", target: "zip" },
197
+ ],
198
+ inputSchema: z.object({
199
+ input: z.object({
200
+ files: z.array(z.instanceof(File)),
201
+ }),
202
+ }),
203
+ outputSchema: z.object({
204
+ archiveUrl: z.string(),
205
+ }),
206
+ });
207
+ });
208
+
209
+ // Create server with both plugins
210
+ const server = await createTypeSafeServer({
211
+ plugins: [
212
+ sharpImagePlugin, // Provides ImagePlugin
213
+ jsZipPlugin, // Provides ZipPlugin
214
+ ] as const, // ✅ Both requirements satisfied
215
+ flows: imageArchiveFlow,
216
+ adapter: honoAdapter({ /* ... */ }),
217
+ dataStore: { type: "s3", config: { bucket: "uploads" } },
218
+ kvStore: redisKvStore,
219
+ });
220
+ ```
221
+
222
+ ## Compile-Time Validation
223
+
224
+ ### ✅ Valid Configuration
225
+
226
+ ```typescript
227
+ import { ImagePlugin } from "@uploadista/core/flow";
228
+ import { sharpImagePlugin } from "@uploadista/flow-images-sharp";
229
+
230
+ // TypeScript validates this successfully
231
+ const server = await createTypeSafeServer({
232
+ plugins: [sharpImagePlugin] as const,
233
+ flows: defineFlow<ImagePlugin>((flowId, clientId) =>
234
+ Effect.gen(function* () {
235
+ const imageService = yield* ImagePlugin; // ✅ Available
236
+ return createFlow({ /* ... */ });
237
+ }),
238
+ ),
239
+ // ... rest of config
240
+ });
241
+ ```
242
+
243
+ ### ❌ Invalid Configuration (Compile Error)
244
+
245
+ ```typescript
246
+ import { ImagePlugin } from "@uploadista/core/flow";
247
+
248
+ // TypeScript ERROR: ImagePlugin required but not provided
249
+ const server = await createTypeSafeServer({
250
+ plugins: [] as const, // ❌ No plugins!
251
+ flows: defineFlow<ImagePlugin>((flowId, clientId) =>
252
+ Effect.gen(function* () {
253
+ const imageService = yield* ImagePlugin; // ❌ Not available
254
+ return createFlow({ /* ... */ });
255
+ }),
256
+ ),
257
+ // ... rest of config
258
+ });
259
+ // Error: Type 'ImagePlugin' does not satisfy constraint 'never'
260
+ ```
261
+
262
+ ### Error Messages
263
+
264
+ When plugins are missing, TypeScript shows helpful error:
265
+
266
+ ```typescript
267
+ type Error = {
268
+ __error: "Missing required plugins";
269
+ __required: ImagePlugin | ZipPlugin; // What's needed
270
+ __provided: never; // What's provided
271
+ __missing: ImagePlugin | ZipPlugin; // What's missing
272
+ };
273
+ ```
274
+
275
+ ## Mixed Flows (Some with Plugins, Some without)
276
+
277
+ ### Router Pattern
278
+
279
+ ```typescript
280
+ import {
281
+ ImagePlugin,
282
+ ZipPlugin,
283
+ CredentialProvider,
284
+ } from "@uploadista/core/flow";
285
+ import {
286
+ defineFlow,
287
+ defineSimpleFlow,
288
+ createTypeSafeServer,
289
+ } from "@uploadista/server";
290
+
291
+ // Different flows with different requirements
292
+ const simpleFlow = defineSimpleFlow((flowId, clientId) => /* ... */);
293
+ const imageFlow = defineFlow<ImagePlugin>((flowId, clientId) => /* ... */);
294
+ const aiFlow = defineFlow<ImagePlugin | CredentialProvider>(
295
+ (flowId, clientId) => /* ... */,
296
+ );
297
+ const archiveFlow = defineFlow<ImagePlugin | ZipPlugin>(
298
+ (flowId, clientId) => /* ... */,
299
+ );
300
+
301
+ // Router function that combines all flows
302
+ // Requirements = union of all individual requirements
303
+ const flowRouter: TypeSafeFlowFunction<
304
+ ImagePlugin | ZipPlugin | CredentialProvider
305
+ > = (flowId, clientId) =>
306
+ Effect.gen(function* () {
307
+ switch (flowId) {
308
+ case "simple":
309
+ return yield* simpleFlow(flowId, clientId);
310
+ case "image":
311
+ return yield* imageFlow(flowId, clientId);
312
+ case "ai":
313
+ return yield* aiFlow(flowId, clientId);
314
+ case "archive":
315
+ return yield* archiveFlow(flowId, clientId);
316
+ default:
317
+ return yield* Effect.fail(
318
+ new UploadistaError({
319
+ code: "FLOW_NOT_FOUND",
320
+ message: `Flow ${flowId} not found`,
321
+ }),
322
+ );
323
+ }
324
+ });
325
+
326
+ // Server must provide ALL plugins used by ANY flow
327
+ const server = await createTypeSafeServer({
328
+ plugins: [
329
+ sharpImagePlugin, // For image flows
330
+ jsZipPlugin, // For archive flow
331
+ credentialProviderLive, // For AI flow
332
+ ] as const,
333
+ flows: flowRouter,
334
+ // ... rest of config
335
+ });
336
+ ```
337
+
338
+ ## Migration from Untyped to Typed
339
+
340
+ ### Before (Untyped)
341
+
342
+ ```typescript
343
+ import { createUploadistaServer } from "@uploadista/server";
344
+
345
+ const server = await createUploadistaServer({
346
+ plugins: [sharpImagePlugin], // No type safety
347
+ flows: (flowId, clientId) =>
348
+ Effect.gen(function* () {
349
+ // Could forget to provide plugin - runtime error!
350
+ const imageService = yield* ImagePlugin;
351
+ return createFlow({ /* ... */ });
352
+ }),
353
+ // ...
354
+ });
355
+ ```
356
+
357
+ ### After (Type-Safe)
358
+
359
+ ```typescript
360
+ import { createTypeSafeServer, defineFlow } from "@uploadista/server";
361
+ import { ImagePlugin } from "@uploadista/core/flow";
362
+
363
+ const imageFlow = defineFlow<ImagePlugin>((flowId, clientId) =>
364
+ Effect.gen(function* () {
365
+ // Type-checked at compile time!
366
+ const imageService = yield* ImagePlugin;
367
+ return createFlow({ /* ... */ });
368
+ }),
369
+ );
370
+
371
+ const server = await createTypeSafeServer({
372
+ plugins: [sharpImagePlugin] as const, // ✅ Type-safe
373
+ flows: imageFlow,
374
+ // ...
375
+ });
376
+ ```
377
+
378
+ ### Step-by-Step Migration
379
+
380
+ 1. **Add explicit type to flow function**:
381
+ ```typescript
382
+ - const myFlow = (flowId, clientId) => ...
383
+ + const myFlow: TypeSafeFlowFunction<ImagePlugin> = (flowId, clientId) => ...
384
+ ```
385
+
386
+ 2. **Use `as const` for plugins array**:
387
+ ```typescript
388
+ - plugins: [sharpImagePlugin]
389
+ + plugins: [sharpImagePlugin] as const
390
+ ```
391
+
392
+ 3. **Replace `createUploadistaServer` with `createTypeSafeServer`**:
393
+ ```typescript
394
+ - import { createUploadistaServer } from "@uploadista/server";
395
+ + import { createTypeSafeServer } from "@uploadista/server";
396
+
397
+ - const server = await createUploadistaServer({
398
+ + const server = await createTypeSafeServer({
399
+ ```
400
+
401
+ 4. **Fix any type errors** (missing plugins, wrong types, etc.)
402
+
403
+ ## Benefits
404
+
405
+ ### Type Safety
406
+
407
+ - ✅ **Compile-time validation** - Errors caught before runtime
408
+ - ✅ **Autocomplete** - IDE suggests available plugin services
409
+ - ✅ **Refactoring safety** - Type errors when removing required plugins
410
+ - ✅ **Documentation** - Types serve as inline documentation
411
+
412
+ ### Before vs After
413
+
414
+ | Aspect | Before (Untyped) | After (Type-Safe) |
415
+ |--------|------------------|-------------------|
416
+ | Plugin validation | Runtime | Compile-time |
417
+ | IDE autocomplete | Limited | Full support |
418
+ | Error messages | Generic | Specific (shows missing plugins) |
419
+ | Refactoring | Risky | Safe |
420
+ | Documentation | External | In types |
421
+
422
+ ## Best Practices
423
+
424
+ 1. **Always use `as const`** for plugin arrays to preserve tuple types
425
+ 2. **Use `defineFlow<T>`** helper for explicit type declarations
426
+ 3. **Declare requirements at flow level**, not server level
427
+ 4. **Group flows by plugin requirements** for better organization
428
+ 5. **Start simple** - Begin with untyped, migrate to typed gradually
429
+
430
+ ## Troubleshooting
431
+
432
+ ### Problem: "Type 'X' does not satisfy constraint 'never'"
433
+
434
+ **Cause**: Flow requires plugin X, but it's not provided.
435
+
436
+ **Solution**: Add the missing plugin to the `plugins` array:
437
+ ```typescript
438
+ plugins: [missingPlugin, ...otherPlugins] as const
439
+ ```
440
+
441
+ ### Problem: "Type instantiation is excessively deep"
442
+
443
+ **Cause**: Too many plugins or complex plugin combinations.
444
+
445
+ **Solution**: Use the untyped `createUploadistaServer` for very complex cases, or split into multiple servers.
446
+
447
+ ### Problem: Lost autocomplete for plugin services
448
+
449
+ **Cause**: Missing `as const` on plugins array.
450
+
451
+ **Solution**: Always add `as const`:
452
+ ```typescript
453
+ plugins: [plugin1, plugin2] as const
454
+ ```
455
+
456
+ ### Problem: Type errors in working code
457
+
458
+ **Cause**: Plugin tuple not properly inferred.
459
+
460
+ **Solution**: Explicitly annotate the type:
461
+ ```typescript
462
+ import type { ImagePluginLayer, ZipPluginLayer } from "@uploadista/core/flow";
463
+
464
+ const plugins: readonly [ImagePluginLayer, ZipPluginLayer] = [
465
+ sharpImagePlugin,
466
+ jsZipPlugin,
467
+ ] as const;
468
+ ```
package/dist/index.cjs CHANGED
@@ -1 +1,2 @@
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;
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()}),ee=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}}),z=({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()}}}),B=({uploadId:e})=>t.Effect.gen(function*(){return{status:200,body:yield*(yield*c.UploadServer).getUpload(e)}}),V=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}}),H=e=>t.Effect.gen(function*(){switch(e.type){case`create-upload`:return yield*ee(e);case`get-capabilities`:return yield*z(e);case`get-upload`:return yield*B(e);case`upload-chunk`:return yield*V(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`}}}}),U=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*H(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()}};async function W(e){return U(e)}function G(e){return e}function K(e){return e}const q={ImagePlugin:{packageName:`@uploadista/flow-images-sharp`,variableName:`sharpImagePlugin`},ImageAiPlugin:{packageName:`@uploadista/flow-images-replicate`,variableName:`replicateImagePlugin`},ZipPlugin:{packageName:`@uploadista/flow-utility-zipjs`,variableName:`zipPlugin`},CredentialProvider:{packageName:`@uploadista/core`,variableName:`credentialProviderLayer`}};function J(e){try{let t=e;if(t._tag)return t._tag;if(t.constructor?.name)return t.constructor.name;if(t.context?.services){let e=Array.from(t.context.services.keys());if(e.length>0){let t=e[0];if(t.key)return t.key}}return null}catch{return null}}function Y(e){return e.map(e=>J(e)).filter(e=>e!==null)}function X(e){let{plugins:t,expectedServices:n=[]}=e,r=Y(t),i=n.filter(e=>!r.includes(e));return i.length===0?{success:!0}:{success:!1,required:n,provided:r,missing:i,suggestions:i.map(e=>{let t=q[e];return t?{name:e,packageName:t.packageName,importStatement:`import { ${t.variableName} } from '${t.packageName}';`}:null}).filter(e=>e!==null)}}function Z(e){let t=[`Server initialization failed: Missing required plugins`,``,`Required: ${e.required.join(`, `)}`,`Provided: ${e.provided.length>0?e.provided.join(`, `):`(none)`}`,`Missing: ${e.missing.join(`, `)}`,``];if(e.suggestions.length>0){t.push(`Add the missing plugins to your configuration:`),t.push(``);for(let n of e.suggestions)t.push(` ${n.importStatement}`);t.push(``),t.push(` const server = await createUploadistaServer({`),t.push(` plugins: [${[...e.provided,...e.missing.map(e=>q[e]?.variableName||e)].join(`, `)}],`),t.push(` // ...`),t.push(` });`)}else t.push(`Note: Could not determine package names for missing plugins.`),t.push(`Please ensure all required plugin layers are provided.`);return t.join(`
2
+ `)}function Q(e){return t.Effect.sync(()=>{let t=X(e);if(!t.success){let e=Z(t);throw Error(e)}})}function te(e){let t=X(e);if(!t.success){let e=Z(t);throw Error(e)}}const ne=(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)}),re=(e,n)=>t.Effect.gen(function*(){n&&(yield*e.unsubscribeFromFlowEvents(n))}),$=(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)}),ie=(e,n)=>t.Effect.gen(function*(){n&&(yield*e.unsubscribeFromUploadEvents(n))}),ae=(e,n,r)=>{let{connection:i,isFlowRoute:a,isUploadRoute:o,jobId:s,uploadId:c,eventId:u}=e;return t.Effect.gen(function*(){a&&(yield*ne(r,s,i)),o&&(yield*$(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`}))})))},oe=(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`}))}}),se=(e,n,r)=>{let{isFlowRoute:i,isUploadRoute:a,jobId:o,uploadId:s}=e;return t.Effect.gen(function*(){i&&(yield*re(r,o)),a&&(yield*ie(n,s))}).pipe(t.Effect.catchAll(e=>t.Effect.sync(()=>{console.error(`Error unsubscribing from events:`,e instanceof l.UploadistaError?e.body:e)})))},ce=(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.createTypeSafeServer=W,exports.createUploadServerLayer=x,exports.createUploadistaErrorResponseBody=L,exports.createUploadistaServer=U,exports.defineFlow=G,exports.defineSimpleFlow=K,exports.extractFlowAndStorageId=b,exports.extractJobAndNodeId=y,exports.extractJobIdFromStatus=v,exports.extractServiceIdentifiers=Y,exports.formatPluginValidationError=Z,exports.getAuthCredentials=e.t,exports.getLastSegment=m,exports.getRouteSegments=g,exports.handleFlowError=_,exports.handleWebSocketClose=se,exports.handleWebSocketError=ce,exports.handleWebSocketMessage=oe,exports.handleWebSocketOpen=ae,exports.hasBasePath=h,exports.parseUrlSegments=p,exports.validatePluginRequirements=X,exports.validatePluginRequirementsEffect=Q,exports.validatePluginsOrThrow=te;