@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.
@@ -1,4 +1,4 @@
1
- import type { UploadistaError } from "@uploadista/core";
1
+ import type { PluginLayer, UploadistaError } from "@uploadista/core";
2
2
  import { type Flow, FlowProvider, FlowWaitUntil } from "@uploadista/core/flow";
3
3
  import {
4
4
  createDataStoreLayer,
@@ -18,31 +18,113 @@ import { createFlowServerLayer, createUploadServerLayer } from "../layer-utils";
18
18
  import { AuthContextServiceLive } from "../service";
19
19
  import type { AuthContext } from "../types";
20
20
  import { handleUploadistaRequest } from "./http-handlers/http-handlers";
21
+ import type { ExtractFlowPluginRequirements } from "./plugin-types";
21
22
  import type { NotFoundResponse } from "./routes";
22
23
  import type { UploadistaServer, UploadistaServerConfig } from "./types";
23
24
 
24
25
  /**
25
26
  * Creates the unified Uploadista server with framework-specific adapter.
26
27
  *
27
- * This function composes all layers (upload server, flow server, auth, metrics)
28
- * and returns a handler that works with any framework via the provided adapter.
28
+ * This is the single, unified API for creating an Uploadista server. It handles
29
+ * all server initialization, layer composition, and runtime setup.
29
30
  *
30
- * The core server handles:
31
+ * ## Core Responsibilities
32
+ *
33
+ * The server handles:
34
+ * - Layer composition (upload/flow servers, auth cache, metrics, plugins)
31
35
  * - Route parsing and matching
32
36
  * - Auth middleware execution with timeout protection
33
- * - Layer composition (upload/flow servers, auth cache, metrics)
34
37
  * - Error handling and response formatting
35
38
  * - Effect program execution with optional tracing
39
+ * - Plugin validation and dependency injection
40
+ *
41
+ * ## Plugin Validation
42
+ *
43
+ * The server supports two validation approaches:
44
+ *
45
+ * ### 1. Runtime Validation (Recommended for Most Cases)
46
+ *
47
+ * The server relies on Effect-TS's dependency injection to validate plugins
48
+ * at runtime. If a required plugin is missing, Effect will fail with a clear
49
+ * MissingService error.
50
+ *
51
+ * ```typescript
52
+ * const server = await createUploadistaServer({
53
+ * flows: getFlowById,
54
+ * plugins: [sharpImagePlugin, zipPlugin],
55
+ * dataStore: s3DataStore,
56
+ * kvStore: redisKvStore,
57
+ * adapter: honoAdapter({ ... })
58
+ * });
59
+ * // If plugins don't match flow requirements, Effect fails with clear error
60
+ * ```
61
+ *
62
+ * ### 2. Compile-Time Validation (Optional)
63
+ *
64
+ * For IDE feedback during development, use the ValidatePlugins type utility:
65
+ *
66
+ * ```typescript
67
+ * import {
68
+ * createUploadistaServer,
69
+ * ValidatePlugins,
70
+ * ExtractFlowPluginRequirements
71
+ * } from '@uploadista/server';
72
+ *
73
+ * // Extract requirements from flows
74
+ * type Requirements = ExtractFlowPluginRequirements<typeof getFlowById>;
75
+ *
76
+ * // Define plugins
77
+ * const plugins = [sharpImagePlugin, zipPlugin] as const;
78
+ *
79
+ * // Validate at compile time (optional, for IDE feedback)
80
+ * type Validation = ValidatePlugins<typeof plugins, Requirements>;
81
+ * // IDE shows error if plugins don't match requirements
82
+ *
83
+ * const server = await createUploadistaServer({
84
+ * flows: getFlowById,
85
+ * plugins,
86
+ * // ...
87
+ * });
88
+ * ```
89
+ *
90
+ * ### 3. Early Runtime Validation (Optional)
91
+ *
92
+ * For better error messages before server starts:
93
+ *
94
+ * ```typescript
95
+ * import { validatePluginsOrThrow } from '@uploadista/server/core';
96
+ *
97
+ * validatePluginsOrThrow({
98
+ * plugins: [sharpImagePlugin],
99
+ * expectedServices: ['ImagePlugin', 'ZipPlugin']
100
+ * });
101
+ * // Throws with helpful error message including import suggestions
102
+ * ```
103
+ *
104
+ * ## Type Safety
105
+ *
106
+ * - Plugin requirements are inferred from flow definitions
107
+ * - Effect-TS ensures dependencies are satisfied at runtime
108
+ * - Type casting is intentional (see inline docs for rationale)
109
+ * - Optional compile-time validation available via type utilities
110
+ *
111
+ * @template TContext - Framework-specific context type
112
+ * @template TResponse - Framework-specific response type
113
+ * @template TWebSocketHandler - WebSocket handler type (if supported)
114
+ * @template TFlows - Flow function type with plugin requirements
115
+ * @template TPlugins - Tuple of plugin layers provided
36
116
  *
37
117
  * @param config - Server configuration including adapter and business logic
38
- * @returns Object with handler function, optional WebSocket handler, and base URL
118
+ * @returns Promise resolving to server instance with handler and metadata
39
119
  *
40
- * @example
120
+ * @example Basic Usage
41
121
  * ```typescript
42
122
  * import { createUploadistaServer, honoAdapter } from "@uploadista/server";
123
+ * import { sharpImagePlugin } from "@uploadista/flow-images-sharp";
43
124
  *
44
125
  * const server = await createUploadistaServer({
45
126
  * flows: getFlowById,
127
+ * plugins: [sharpImagePlugin],
46
128
  * dataStore: { type: "s3", config: { bucket: "uploads" } },
47
129
  * kvStore: redisKvStore,
48
130
  * adapter: honoAdapter({
@@ -53,16 +135,53 @@ import type { UploadistaServer, UploadistaServerConfig } from "./types";
53
135
  * // Use with Hono
54
136
  * app.all("/uploadista/*", server.handler);
55
137
  * ```
138
+ *
139
+ * @example With Compile-Time Validation
140
+ * ```typescript
141
+ * import {
142
+ * createUploadistaServer,
143
+ * ValidatePlugins,
144
+ * ExtractFlowPluginRequirements
145
+ * } from "@uploadista/server";
146
+ *
147
+ * type Requirements = ExtractFlowPluginRequirements<typeof getFlowById>;
148
+ * const plugins = [sharpImagePlugin, zipPlugin] as const;
149
+ * type Validation = ValidatePlugins<typeof plugins, Requirements>;
150
+ *
151
+ * const server = await createUploadistaServer({
152
+ * flows: getFlowById,
153
+ * plugins,
154
+ * // ... rest of config
155
+ * });
156
+ * ```
157
+ *
158
+ * @see ValidatePlugins - Compile-time plugin validation
159
+ * @see ExtractFlowPluginRequirements - Extract requirements from flows
160
+ * @see validatePluginRequirements - Runtime validation helper
161
+ * @see API_DECISION_GUIDE.md - Complete guide for choosing validation approach
56
162
  */
57
163
  export const createUploadistaServer = async <
58
164
  TContext,
59
165
  TResponse,
60
166
  TWebSocketHandler = unknown,
167
+ TFlows extends (
168
+ flowId: string,
169
+ clientId: string | null,
170
+ ) => Effect.Effect<
171
+ // biome-ignore lint/suspicious/noExplicitAny: Flow requirements can be any plugin services
172
+ Flow<z.ZodSchema<unknown>, z.ZodSchema<unknown>, any>,
173
+ UploadistaError,
174
+ // biome-ignore lint/suspicious/noExplicitAny: Flow return type allows any requirements
175
+ any
176
+ // biome-ignore lint/suspicious/noExplicitAny: Generic type constraint allows any flow function type with any requirements
177
+ > = any,
178
+ TPlugins extends readonly PluginLayer[] = readonly PluginLayer[],
61
179
  >({
62
180
  flows,
63
181
  dataStore,
64
182
  kvStore,
65
- plugins = [],
183
+ // Default to an empty plugin list while preserving the generic type
184
+ plugins = [] as unknown as TPlugins,
66
185
  eventEmitter,
67
186
  eventBroadcaster = memoryEventBroadcaster,
68
187
  withTracing = false,
@@ -72,9 +191,13 @@ export const createUploadistaServer = async <
72
191
  bufferedDataStore,
73
192
  adapter,
74
193
  authCacheConfig,
75
- }: UploadistaServerConfig<TContext, TResponse, TWebSocketHandler>): Promise<
76
- UploadistaServer<TContext, TResponse, TWebSocketHandler>
77
- > => {
194
+ }: UploadistaServerConfig<
195
+ TContext,
196
+ TResponse,
197
+ TWebSocketHandler,
198
+ TFlows,
199
+ TPlugins
200
+ >): Promise<UploadistaServer<TContext, TResponse, TWebSocketHandler>> => {
78
201
  // Default eventEmitter to webSocketEventEmitter with the provided eventBroadcaster
79
202
  const finalEventEmitter =
80
203
  eventEmitter ?? webSocketEventEmitter(eventBroadcaster);
@@ -84,6 +207,8 @@ export const createUploadistaServer = async <
84
207
  ? configBaseUrl.slice(0, -1)
85
208
  : configBaseUrl;
86
209
 
210
+ type FlowReq = ExtractFlowPluginRequirements<TFlows>;
211
+
87
212
  // Create flow provider layer from flows function
88
213
  const flowProviderLayer = Layer.effect(
89
214
  FlowProvider,
@@ -92,9 +217,8 @@ export const createUploadistaServer = async <
92
217
  // Cast the flows function to match FlowProvider expectations
93
218
  // The context requirements will be provided at the layer level
94
219
  return flows(flowId, clientId) as Effect.Effect<
95
- Flow<z.ZodSchema<unknown>, z.ZodSchema<unknown>, unknown>,
96
- UploadistaError,
97
- never
220
+ Flow<z.ZodSchema<unknown>, z.ZodSchema<unknown>, FlowReq>,
221
+ UploadistaError
98
222
  >;
99
223
  },
100
224
  }),
@@ -137,7 +261,13 @@ export const createUploadistaServer = async <
137
261
  // Metrics layer (defaults to NoOp if not provided)
138
262
  const effectiveMetricsLayer = metricsLayer ?? NoOpMetricsServiceLive;
139
263
 
140
- const serverLayer = Layer.mergeAll(
264
+ /**
265
+ * Merge all server layers including plugins.
266
+ *
267
+ * This combines the core server infrastructure (upload server, flow server,
268
+ * metrics, auth cache) with user-provided plugin layers.
269
+ */
270
+ const serverLayerRaw = Layer.mergeAll(
141
271
  uploadServerLayer,
142
272
  flowServerLayer,
143
273
  effectiveMetricsLayer,
@@ -145,9 +275,94 @@ export const createUploadistaServer = async <
145
275
  ...plugins,
146
276
  );
147
277
 
278
+ /**
279
+ * Type Casting Rationale for Plugin System
280
+ *
281
+ * The type assertion below is intentional and safe. This is not a bug or workaround,
282
+ * but follows Effect-TS's design for dynamic dependency injection.
283
+ *
284
+ * ## Why Type Casting is Necessary
285
+ *
286
+ * 1. **Plugin Requirements are Dynamic**
287
+ * Different flows require different plugins (ImagePlugin, ZipPlugin, etc.).
288
+ * These requirements are only known when flows are loaded at runtime.
289
+ * Flow A might need ImagePlugin, Flow B might need ZipPlugin.
290
+ *
291
+ * 2. **TypeScript's Static Limitation**
292
+ * TypeScript cannot statically verify that all possible flow combinations
293
+ * will have their requirements satisfied. The plugin array is typed as
294
+ * `readonly PluginLayer[]` which could be any combination of plugins.
295
+ *
296
+ * 3. **Effect-TS Runtime Resolution**
297
+ * Effect-TS is designed to resolve service requirements at runtime using
298
+ * its dependency injection system. When a flow executes and accesses a service:
299
+ *
300
+ * ```typescript
301
+ * const imagePlugin = yield* ImagePlugin;
302
+ * ```
303
+ *
304
+ * Effect checks if ImagePlugin exists in the provided layer context.
305
+ * If missing, Effect fails with a clear MissingService error.
306
+ *
307
+ * 4. **Layer Composition Guarantees**
308
+ * Layer.mergeAll() combines all layers. At runtime, Effect ensures that
309
+ * when a service is requested, it's either:
310
+ * - Provided by one of the merged layers, OR
311
+ * - Results in a MissingService error with the service name
312
+ *
313
+ * ## Safety Guarantees
314
+ *
315
+ * This pattern is safe because:
316
+ *
317
+ * 1. **Runtime Validation** (Optional but Recommended)
318
+ * We provide validatePluginRequirements() that checks plugins before
319
+ * server initialization, giving excellent error messages early.
320
+ *
321
+ * 2. **Effect's Built-in Validation**
322
+ * If runtime validation is skipped, Effect will fail during flow execution
323
+ * with a MissingService error containing the service identifier.
324
+ *
325
+ * 3. **Optional Compile-Time Validation**
326
+ * Developers can use ValidatePlugins<> type utility for IDE feedback:
327
+ *
328
+ * ```typescript
329
+ * type Validation = ValidatePlugins<typeof plugins, Requirements>;
330
+ * // Shows compile error if plugins don't match requirements
331
+ * ```
332
+ *
333
+ * 4. **No Silent Failures**
334
+ * There's no scenario where missing plugins cause silent failures.
335
+ * Either runtime validation catches it, or Effect fails with clear error.
336
+ *
337
+ * ## This is Effect-TS's Idiomatic Pattern
338
+ *
339
+ * Effect-TS separates compile-time structure from runtime resolution:
340
+ * - Compile-time: Types ensure layer structure is correct
341
+ * - Runtime: Effect resolves actual dependencies and fails if missing
342
+ *
343
+ * The type system provides structure and IDE support, while Effect's
344
+ * runtime handles actual requirement resolution.
345
+ *
346
+ * ## Further Reading
347
+ *
348
+ * - Effect-TS Context Management: https://effect.website/docs/guides/context-management
349
+ * - Runtime Validation: See plugin-validation.ts for helper functions
350
+ * - Type Utilities: See plugin-types.ts for compile-time validation
351
+ *
352
+ * @see validatePluginRequirements - Runtime validation helper
353
+ * @see ValidatePlugins - Compile-time validation type utility
354
+ */
355
+ const serverLayer = serverLayerRaw as unknown as Layer.Layer<
356
+ // biome-ignore lint/suspicious/noExplicitAny: Dynamic plugin requirements require any - see comprehensive explanation above
357
+ any,
358
+ never,
359
+ never
360
+ >;
361
+
148
362
  // Create a shared managed runtime from the server layer
149
363
  // This ensures all requests use the same layer instances (including event broadcaster)
150
364
  // ManagedRuntime properly handles scoped resources and provides convenient run methods
365
+
151
366
  const managedRuntime = ManagedRuntime.make(serverLayer);
152
367
 
153
368
  /**
@@ -247,6 +462,7 @@ export const createUploadistaServer = async <
247
462
 
248
463
  // Extract waitUntil callback if available (for Cloudflare Workers)
249
464
  // This must be extracted per-request since it comes from the framework context
465
+ // biome-ignore lint/suspicious/noExplicitAny: Layer array needs to accept any service type from waitUntil
250
466
  const waitUntilLayers: Layer.Layer<any, never, never>[] = [];
251
467
  if (adapter.extractWaitUntil) {
252
468
  const waitUntilCallback = adapter.extractWaitUntil(ctx);
package/src/core/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { UploadistaError } from "@uploadista/core";
1
+ import type { PluginLayer, UploadistaError } from "@uploadista/core";
2
2
  import type { Flow } from "@uploadista/core/flow";
3
3
  import type {
4
4
  BaseEventEmitterService,
@@ -9,6 +9,7 @@ import type {
9
9
  UploadFileKVStore,
10
10
  } from "@uploadista/core/types";
11
11
  import type { GenerateId } from "@uploadista/core/utils";
12
+ import type { MetricsService } from "@uploadista/observability";
12
13
  import type { Effect, Layer } from "effect";
13
14
  import type { z } from "zod";
14
15
  import type { ServerAdapter } from "../adapter";
@@ -56,6 +57,8 @@ export type FlowsFunction = (
56
57
  * @template TRequest - Framework-specific request type
57
58
  * @template TResponse - Framework-specific response type
58
59
  * @template TWebSocket - Framework-specific WebSocket type (optional)
60
+ * @template TFlows - Function type for retrieving flows
61
+ * @template TPlugins - Tuple of plugin layers that provide requirements for flows
59
62
  *
60
63
  * @example
61
64
  * ```typescript
@@ -84,6 +87,18 @@ export interface UploadistaServerConfig<
84
87
  TRequest,
85
88
  TResponse,
86
89
  TWebSocket = unknown,
90
+ TFlows extends (
91
+ flowId: string,
92
+ clientId: string | null,
93
+ ) => Effect.Effect<
94
+ // biome-ignore lint/suspicious/noExplicitAny: Flow requirements can be any plugin services
95
+ Flow<z.ZodSchema<unknown>, z.ZodSchema<unknown>, any>,
96
+ UploadistaError,
97
+ // biome-ignore lint/suspicious/noExplicitAny: Flow return type allows any requirements
98
+ any
99
+ // biome-ignore lint/suspicious/noExplicitAny: Generic type constraint allows any flow function type with any requirements
100
+ > = any,
101
+ TPlugins extends readonly PluginLayer[] = readonly PluginLayer[],
87
102
  > {
88
103
  /**
89
104
  * Function for retrieving flows by ID.
@@ -97,7 +112,7 @@ export interface UploadistaServerConfig<
97
112
  * flows: (flowId, clientId) => Effect.succeed(myFlows[flowId])
98
113
  * ```
99
114
  */
100
- flows: FlowsFunction;
115
+ flows: TFlows;
101
116
 
102
117
  /**
103
118
  * Data store configuration for file storage.
@@ -144,8 +159,7 @@ export interface UploadistaServerConfig<
144
159
  * plugins: [imageProcessingPlugin, virusScanPlugin]
145
160
  * ```
146
161
  */
147
- // biome-ignore lint/suspicious/noExplicitAny: Permissive constraint allows plugin tuples
148
- plugins?: readonly Layer.Layer<any, never, never>[];
162
+ plugins?: TPlugins;
149
163
 
150
164
  /**
151
165
  * Optional: Event emitter layer for progress notifications.
@@ -227,8 +241,7 @@ export interface UploadistaServerConfig<
227
241
  * metricsLayer: prometheusMetrics()
228
242
  * ```
229
243
  */
230
- // biome-ignore lint/suspicious/noExplicitAny: MetricsService is defined in @uploadista/observability
231
- metricsLayer?: Layer.Layer<any, never, never>;
244
+ metricsLayer?: Layer.Layer<MetricsService, never, never>;
232
245
 
233
246
  /**
234
247
  * Optional: Buffered data store layer for performance optimization.
@@ -1,57 +1,139 @@
1
+ /**
2
+ * @deprecated This module is deprecated and will be removed in a future version.
3
+ *
4
+ * Please migrate to the new consolidated plugin types from `@uploadista/server/core/plugin-types`:
5
+ *
6
+ * Migration guide:
7
+ * - `LayerSuccessUnion<T>` → `ExtractLayerServices<T>` from `@uploadista/core/flow/types`
8
+ * - `FlowRequirementsOf<T>` → `ExtractFlowPluginRequirements<T>` from `@uploadista/server/core/plugin-types`
9
+ * - `RequiredPluginsOf<T>` → `ExtractFlowPluginRequirements<T>` from `@uploadista/server/core/plugin-types`
10
+ * - `PluginAssertion<TFlows, TPlugins>` → `ValidatePlugins<TPlugins, TRequirements>` from `@uploadista/server/core/plugin-types`
11
+ *
12
+ * See the migration guide in the documentation for more details.
13
+ *
14
+ * @module plugins-typing
15
+ */
16
+
1
17
  import type { Flow, UploadServer } from "@uploadista/core";
18
+ import type { ExtractLayerServices } from "@uploadista/core/flow/types";
2
19
  import type { Effect, Layer } from "effect";
3
20
  import type z from "zod";
4
21
 
22
+ /**
23
+ * @deprecated Use `ExtractLayerServices` from `@uploadista/core/flow/types` instead.
24
+ *
25
+ * Extracts service types from a tuple of layers.
26
+ *
27
+ * @example Migration
28
+ * ```typescript
29
+ * // Old
30
+ * import { LayerSuccessUnion } from '@uploadista/server/plugins-typing';
31
+ * type Services = LayerSuccessUnion<[ImagePluginLayer, ZipPluginLayer]>;
32
+ *
33
+ * // New
34
+ * import { ExtractLayerServices } from '@uploadista/core/flow/types';
35
+ * type Services = ExtractLayerServices<[ImagePluginLayer, ZipPluginLayer]>;
36
+ * ```
37
+ */
5
38
  export type LayerSuccessUnion<
6
- Layers extends readonly Layer.Layer<any, never, never>[],
7
- > = Layers[number] extends Layer.Layer<infer Success, never, never>
8
- ? Success
9
- : never;
39
+ Layers extends readonly Layer.Layer<any, never, never>[],
40
+ > = ExtractLayerServices<Layers>;
10
41
 
42
+ /**
43
+ * @deprecated This type is deprecated. Extract flow requirements directly from your flow types instead.
44
+ *
45
+ * Extracts the success type from a flow function's Effect return value.
46
+ */
11
47
  export type FlowSuccess<
12
- TFlows extends (
13
- flowId: string,
14
- clientId: string | null,
15
- ) => Effect.Effect<unknown, unknown, unknown>,
48
+ TFlows extends (
49
+ flowId: string,
50
+ clientId: string | null,
51
+ ) => Effect.Effect<unknown, unknown, unknown>,
16
52
  > = ReturnType<TFlows> extends Effect.Effect<infer Success, unknown, unknown>
17
- ? Success
18
- : never;
53
+ ? Success
54
+ : never;
19
55
 
56
+ /**
57
+ * @deprecated Use `ExtractFlowPluginRequirements` from `@uploadista/server/core/plugin-types` instead.
58
+ *
59
+ * Extracts plugin requirements from a flow function, excluding UploadServer.
60
+ *
61
+ * @example Migration
62
+ * ```typescript
63
+ * // Old
64
+ * import { FlowRequirementsOf } from '@uploadista/server/plugins-typing';
65
+ * type Requirements = FlowRequirementsOf<typeof myFlowFunction>;
66
+ *
67
+ * // New
68
+ * import { ExtractFlowPluginRequirements } from '@uploadista/server/core/plugin-types';
69
+ * type Requirements = ExtractFlowPluginRequirements<typeof myFlowFunction>;
70
+ * ```
71
+ */
20
72
  export type FlowRequirementsOf<
21
- TFlows extends (
22
- flowId: string,
23
- clientId: string | null,
24
- ) => Effect.Effect<unknown, unknown, unknown>,
73
+ TFlows extends (
74
+ flowId: string,
75
+ clientId: string | null,
76
+ ) => Effect.Effect<unknown, unknown, unknown>,
25
77
  > = FlowSuccess<TFlows> extends Flow<
26
- z.ZodSchema<unknown>,
27
- z.ZodSchema<unknown>,
28
- infer R
78
+ z.ZodSchema<unknown>,
79
+ z.ZodSchema<unknown>,
80
+ infer R
29
81
  >
30
- ? Exclude<R, UploadServer>
31
- : never;
82
+ ? Exclude<R, UploadServer>
83
+ : never;
32
84
 
85
+ /**
86
+ * @deprecated Use `ExtractFlowPluginRequirements` from `@uploadista/server/core/plugin-types` instead.
87
+ *
88
+ * This is an alias for FlowRequirementsOf and provides the same functionality.
89
+ *
90
+ * @example Migration
91
+ * ```typescript
92
+ * // Old
93
+ * import { RequiredPluginsOf } from '@uploadista/server/plugins-typing';
94
+ * type Requirements = RequiredPluginsOf<typeof myFlowFunction>;
95
+ *
96
+ * // New
97
+ * import { ExtractFlowPluginRequirements } from '@uploadista/server/core/plugin-types';
98
+ * type Requirements = ExtractFlowPluginRequirements<typeof myFlowFunction>;
99
+ * ```
100
+ */
33
101
  export type RequiredPluginsOf<
34
- TFlows extends (
35
- flowId: string,
36
- clientId: string | null,
37
- ) => Effect.Effect<unknown, unknown, unknown>,
102
+ TFlows extends (
103
+ flowId: string,
104
+ clientId: string | null,
105
+ ) => Effect.Effect<unknown, unknown, unknown>,
38
106
  > = Exclude<FlowRequirementsOf<TFlows>, UploadServer>;
39
107
 
108
+ /**
109
+ * @deprecated Use `ValidatePlugins` from `@uploadista/server/core/plugin-types` instead.
110
+ *
111
+ * This type validates plugin requirements at compile time.
112
+ *
113
+ * @example Migration
114
+ * ```typescript
115
+ * // Old
116
+ * import { PluginAssertion } from '@uploadista/server/plugins-typing';
117
+ * type Validation = PluginAssertion<typeof myFlowFunction, typeof plugins>;
118
+ *
119
+ * // New
120
+ * import { ValidatePlugins, ExtractFlowPluginRequirements } from '@uploadista/server/core/plugin-types';
121
+ * type Requirements = ExtractFlowPluginRequirements<typeof myFlowFunction>;
122
+ * type Validation = ValidatePlugins<typeof plugins, Requirements>;
123
+ * ```
124
+ */
40
125
  export type PluginAssertion<
41
- TFlows extends (
42
- flowId: string,
43
- clientId: string | null,
44
- ) => Effect.Effect<unknown, unknown, unknown>,
45
- // biome-ignore lint/suspicious/noExplicitAny: Permissive constraint allows plugin tuples where each plugin provides subset of requirements
46
- TPlugins extends readonly Layer.Layer<any, never, never>[],
47
- > = Exclude<
48
- RequiredPluginsOf<TFlows>,
49
- LayerSuccessUnion<TPlugins>
50
- > extends never
51
- ? unknown
52
- : {
53
- __missingPlugins: Exclude<
54
- RequiredPluginsOf<TFlows>,
55
- LayerSuccessUnion<TPlugins>
56
- >;
57
- };
126
+ TFlows extends (
127
+ flowId: string,
128
+ clientId: string | null,
129
+ ) => Effect.Effect<unknown, unknown, unknown>,
130
+ // biome-ignore lint/suspicious/noExplicitAny: Permissive constraint allows plugin tuples where each plugin provides subset of requirements
131
+ TPlugins extends readonly Layer.Layer<any, never, never>[],
132
+ > = Exclude<RequiredPluginsOf<TFlows>, LayerSuccessUnion<TPlugins>> extends never
133
+ ? unknown
134
+ : {
135
+ __missingPlugins: Exclude<
136
+ RequiredPluginsOf<TFlows>,
137
+ LayerSuccessUnion<TPlugins>
138
+ >;
139
+ };