@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.
- package/ADVANCED_TYPE_SYSTEM.md +495 -0
- package/PLUGIN_TYPING.md +369 -0
- package/TYPE_SAFE_EXAMPLES.md +468 -0
- package/dist/index.cjs +2 -1
- package/dist/index.d.cts +639 -17
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +638 -16
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
- package/src/__tests__/backward-compatibility.test.ts +285 -0
- package/src/core/__tests__/plugin-validation.test.ts +472 -0
- package/src/core/create-type-safe-server.ts +204 -0
- package/src/core/index.ts +3 -0
- package/src/core/plugin-types.ts +217 -0
- package/src/core/plugin-validation.ts +319 -0
- package/src/core/server.ts +231 -15
- package/src/core/types.ts +19 -6
- package/src/plugins-typing.ts +122 -40
- package/type-tests/plugin-types.test-d.ts +388 -0
package/src/core/server.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, 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
|
|
28
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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
|
-
|
|
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<
|
|
76
|
-
|
|
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>,
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
package/src/plugins-typing.ts
CHANGED
|
@@ -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
|
-
|
|
7
|
-
> = Layers
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
18
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
73
|
+
TFlows extends (
|
|
74
|
+
flowId: string,
|
|
75
|
+
clientId: string | null,
|
|
76
|
+
) => Effect.Effect<unknown, unknown, unknown>,
|
|
25
77
|
> = FlowSuccess<TFlows> extends Flow<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
78
|
+
z.ZodSchema<unknown>,
|
|
79
|
+
z.ZodSchema<unknown>,
|
|
80
|
+
infer R
|
|
29
81
|
>
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
> = Exclude<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
+
};
|