@uploadista/server 0.0.18-beta.8 → 0.0.18

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/dist/index.d.cts CHANGED
@@ -672,6 +672,168 @@ type InferFlowRequirements<T> = T extends TypeSafeFlowFunction<infer R> ? R : ne
672
672
  */
673
673
  type RuntimePluginLayers<T extends PluginTuple> = { [K in keyof T]: T[K] extends Layer.Layer<infer S, infer E, infer R> ? Layer.Layer<S, E, R> : never };
674
674
  //#endregion
675
+ //#region src/usage-hooks/types.d.ts
676
+ /**
677
+ * Result of a usage hook that can abort processing.
678
+ * Used by onUploadStart and onFlowStart hooks.
679
+ */
680
+ type UsageHookResult = {
681
+ readonly action: "continue";
682
+ } | {
683
+ readonly action: "abort";
684
+ readonly reason: string;
685
+ readonly code?: string;
686
+ };
687
+ /**
688
+ * Helper to create a continue result.
689
+ */
690
+ declare const continueResult: () => UsageHookResult;
691
+ /**
692
+ * Helper to create an abort result.
693
+ */
694
+ declare const abortResult: (reason: string, code?: string) => UsageHookResult;
695
+ /**
696
+ * Base metadata shared across all usage contexts.
697
+ */
698
+ interface BaseUsageMetadata {
699
+ /** File size in bytes (if known) */
700
+ fileSize?: number;
701
+ /** MIME type of the file */
702
+ mimeType?: string;
703
+ /** Original file name */
704
+ fileName?: string;
705
+ }
706
+ /**
707
+ * Metadata specific to upload operations.
708
+ */
709
+ interface UploadUsageMetadata extends BaseUsageMetadata {
710
+ /** Unique upload identifier */
711
+ uploadId?: string;
712
+ /** Duration of the upload in milliseconds */
713
+ duration?: number;
714
+ }
715
+ /**
716
+ * Metadata specific to flow operations.
717
+ */
718
+ interface FlowUsageMetadata extends BaseUsageMetadata {
719
+ /** Flow identifier being executed */
720
+ flowId?: string;
721
+ /** Unique job identifier */
722
+ jobId?: string;
723
+ /** Number of nodes in the flow */
724
+ nodeCount?: number;
725
+ /** Total size of input files */
726
+ inputFileSize?: number;
727
+ /** Total size of output files (on complete) */
728
+ outputSize?: number;
729
+ /** Number of nodes that were executed (on complete) */
730
+ nodesExecuted?: number;
731
+ /** Duration of flow execution in milliseconds (on complete) */
732
+ duration?: number;
733
+ /** Flow completion status (on complete) */
734
+ status?: "success" | "failed" | "cancelled";
735
+ }
736
+ /**
737
+ * Context passed to usage hooks containing client and operation information.
738
+ */
739
+ interface UsageContext<TMetadata extends BaseUsageMetadata = BaseUsageMetadata> {
740
+ /** Organization/client identifier */
741
+ clientId: string;
742
+ /** Type of operation */
743
+ operation: "upload" | "flow";
744
+ /** Operation-specific metadata */
745
+ metadata: TMetadata;
746
+ }
747
+ /**
748
+ * Upload-specific usage context.
749
+ */
750
+ type UploadUsageContext = UsageContext<UploadUsageMetadata>;
751
+ /**
752
+ * Flow-specific usage context.
753
+ */
754
+ type FlowUsageContext = UsageContext<FlowUsageMetadata>;
755
+ /**
756
+ * Hook called before upload processing begins.
757
+ * Can return abort to reject the upload (e.g., quota exceeded).
758
+ */
759
+ type OnUploadStartHook = (ctx: UploadUsageContext) => Effect.Effect<UsageHookResult>;
760
+ /**
761
+ * Hook called after upload completes successfully.
762
+ * Used for recording usage. Errors are logged but don't fail the upload.
763
+ */
764
+ type OnUploadCompleteHook = (ctx: UploadUsageContext) => Effect.Effect<void>;
765
+ /**
766
+ * Hook called before flow execution begins.
767
+ * Can return abort to reject the flow (e.g., subscription expired).
768
+ */
769
+ type OnFlowStartHook = (ctx: FlowUsageContext) => Effect.Effect<UsageHookResult>;
770
+ /**
771
+ * Hook called after flow completes (success, failure, or cancellation).
772
+ * Used for recording usage. Errors are logged but don't fail the response.
773
+ */
774
+ type OnFlowCompleteHook = (ctx: FlowUsageContext) => Effect.Effect<void>;
775
+ /**
776
+ * Configuration for usage tracking hooks.
777
+ * All hooks are optional - unconfigured hooks are no-ops.
778
+ *
779
+ * @example
780
+ * ```typescript
781
+ * const usageHooks: UsageHooks = {
782
+ * onUploadStart: (ctx) => Effect.gen(function* () {
783
+ * const hasQuota = yield* checkQuota(ctx.clientId, ctx.metadata.fileSize);
784
+ * if (!hasQuota) {
785
+ * return abortResult("Storage quota exceeded", "QUOTA_EXCEEDED");
786
+ * }
787
+ * return continueResult();
788
+ * }),
789
+ * onUploadComplete: (ctx) => Effect.gen(function* () {
790
+ * yield* recordUsage(ctx.clientId, ctx.metadata.fileSize);
791
+ * }),
792
+ * };
793
+ * ```
794
+ */
795
+ interface UsageHooks {
796
+ /**
797
+ * Called before upload processing begins.
798
+ * Return abort to reject the upload.
799
+ */
800
+ onUploadStart?: OnUploadStartHook;
801
+ /**
802
+ * Called after upload completes successfully.
803
+ * Errors are logged but don't fail the upload.
804
+ */
805
+ onUploadComplete?: OnUploadCompleteHook;
806
+ /**
807
+ * Called before flow execution begins.
808
+ * Return abort to reject the flow.
809
+ */
810
+ onFlowStart?: OnFlowStartHook;
811
+ /**
812
+ * Called after flow completes (success, failure, or cancellation).
813
+ * Errors are logged but don't fail the response.
814
+ */
815
+ onFlowComplete?: OnFlowCompleteHook;
816
+ }
817
+ /**
818
+ * Configuration for the usage hook service.
819
+ */
820
+ interface UsageHookConfig {
821
+ /**
822
+ * The usage hooks to execute.
823
+ */
824
+ hooks?: UsageHooks;
825
+ /**
826
+ * Timeout for hook execution in milliseconds.
827
+ * If a hook takes longer than this, it will be considered failed.
828
+ * Default: 5000ms (5 seconds)
829
+ */
830
+ timeout?: number;
831
+ }
832
+ /**
833
+ * Default timeout for usage hooks (5 seconds).
834
+ */
835
+ declare const DEFAULT_USAGE_HOOK_TIMEOUT = 5000;
836
+ //#endregion
675
837
  //#region src/core/types.d.ts
676
838
  /**
677
839
  * Function type for retrieving flows based on flow ID and client ID.
@@ -853,6 +1015,40 @@ interface UploadistaServerConfig<TRequest, TResponse, TWebSocket = unknown, TFlo
853
1015
  * @default false
854
1016
  */
855
1017
  withTracing?: boolean;
1018
+ /**
1019
+ * Optional: Custom observability layer for distributed tracing.
1020
+ *
1021
+ * When provided, this layer will be used instead of the default NodeSdkLive.
1022
+ * This allows you to configure custom OTLP exporters (e.g., for Grafana Cloud,
1023
+ * Jaeger, or other OpenTelemetry-compatible backends).
1024
+ *
1025
+ * Requires `withTracing: true` to be effective.
1026
+ *
1027
+ * @example
1028
+ * ```typescript
1029
+ * import { OtlpNodeSdkLive, createOtlpNodeSdkLayer } from "@uploadista/observability";
1030
+ *
1031
+ * // Option 1: Use default OTLP layer (reads from env vars)
1032
+ * const server = await createUploadistaServer({
1033
+ * withTracing: true,
1034
+ * observabilityLayer: OtlpNodeSdkLive,
1035
+ * // ...
1036
+ * });
1037
+ *
1038
+ * // Option 2: Custom configuration with tenant attributes
1039
+ * const server = await createUploadistaServer({
1040
+ * withTracing: true,
1041
+ * observabilityLayer: createOtlpNodeSdkLayer({
1042
+ * serviceName: "uploadista-cloud-api",
1043
+ * resourceAttributes: {
1044
+ * "deployment.environment": "production",
1045
+ * },
1046
+ * }),
1047
+ * // ...
1048
+ * });
1049
+ * ```
1050
+ */
1051
+ observabilityLayer?: Layer.Layer<any, never, never>;
856
1052
  /**
857
1053
  * Optional: Metrics layer for observability.
858
1054
  *
@@ -988,6 +1184,39 @@ interface UploadistaServerConfig<TRequest, TResponse, TWebSocket = unknown, TFlo
988
1184
  * ```
989
1185
  */
990
1186
  healthCheck?: HealthCheckConfig;
1187
+ /**
1188
+ * Optional: Usage hooks for tracking and billing integration.
1189
+ *
1190
+ * Usage hooks allow you to intercept upload and flow operations for:
1191
+ * - Quota checking (e.g., verify user has subscription)
1192
+ * - Usage tracking (e.g., count uploads, track bandwidth)
1193
+ * - Billing integration (e.g., report usage to Stripe/Polar)
1194
+ *
1195
+ * Hooks follow a "fail-open" design - if a hook times out or errors,
1196
+ * the operation proceeds (unless the hook explicitly aborts).
1197
+ *
1198
+ * @example
1199
+ * ```typescript
1200
+ * usageHooks: {
1201
+ * hooks: {
1202
+ * onUploadStart: (ctx) => Effect.gen(function* () {
1203
+ * // Check quota before upload starts
1204
+ * const quota = yield* checkUserQuota(ctx.clientId);
1205
+ * if (quota.exceeded) {
1206
+ * return { action: "abort", reason: "Storage quota exceeded" };
1207
+ * }
1208
+ * return { action: "continue" };
1209
+ * }),
1210
+ * onUploadComplete: (ctx) => Effect.gen(function* () {
1211
+ * // Track usage after upload completes
1212
+ * yield* reportUsage(ctx.clientId, ctx.metadata.fileSize);
1213
+ * }),
1214
+ * },
1215
+ * timeout: 5000, // 5 second timeout for hooks
1216
+ * }
1217
+ * ```
1218
+ */
1219
+ usageHooks?: UsageHookConfig;
991
1220
  }
992
1221
  /**
993
1222
  * Return type from createUploadistaServer.
@@ -1454,6 +1683,7 @@ declare const createUploadistaServer: <TContext, TResponse, TWebSocketHandler =
1454
1683
  eventEmitter,
1455
1684
  eventBroadcaster,
1456
1685
  withTracing,
1686
+ observabilityLayer,
1457
1687
  baseUrl: configBaseUrl,
1458
1688
  generateId,
1459
1689
  metricsLayer,
@@ -1462,7 +1692,8 @@ declare const createUploadistaServer: <TContext, TResponse, TWebSocketHandler =
1462
1692
  authCacheConfig,
1463
1693
  circuitBreaker,
1464
1694
  deadLetterQueue,
1465
- healthCheck
1695
+ healthCheck,
1696
+ usageHooks
1466
1697
  }: UploadistaServerConfig<TContext, TResponse, TWebSocketHandler, TFlows, TPlugins>) => Promise<UploadistaServer<TContext, TResponse, TWebSocketHandler>>;
1467
1698
  //#endregion
1468
1699
  //#region src/core/websocket-routes.d.ts
@@ -1997,6 +2228,295 @@ declare const createFlowServerLayer: ({
1997
2228
  uploadServer
1998
2229
  }: FlowServerLayerConfig) => Layer.Layer<_uploadista_core0.FlowServer, never, never>;
1999
2230
  //#endregion
2231
+ //#region src/permissions/types.d.ts
2232
+ /**
2233
+ * Permission Types and Constants
2234
+ *
2235
+ * Defines the permission model for fine-grained access control in the uploadista engine.
2236
+ * Permissions follow a hierarchical format: `resource:action` with support for wildcards.
2237
+ */
2238
+ /**
2239
+ * Engine permissions for administrative operations.
2240
+ * These control access to health, readiness, metrics, and DLQ endpoints.
2241
+ */
2242
+ declare const ENGINE_PERMISSIONS: {
2243
+ /** Full admin access to all engine operations */
2244
+ readonly ALL: "engine:*";
2245
+ /** Access health endpoint */
2246
+ readonly HEALTH: "engine:health";
2247
+ /** Access readiness endpoint */
2248
+ readonly READINESS: "engine:readiness";
2249
+ /** Access metrics endpoint */
2250
+ readonly METRICS: "engine:metrics";
2251
+ /** Full DLQ access (implies read and write) */
2252
+ readonly DLQ: "engine:dlq";
2253
+ /** Read DLQ entries */
2254
+ readonly DLQ_READ: "engine:dlq:read";
2255
+ /** Retry/delete DLQ entries */
2256
+ readonly DLQ_WRITE: "engine:dlq:write";
2257
+ };
2258
+ /**
2259
+ * Flow permissions for flow execution operations.
2260
+ */
2261
+ declare const FLOW_PERMISSIONS: {
2262
+ /** Full access to all flow operations */
2263
+ readonly ALL: "flow:*";
2264
+ /** Execute flows */
2265
+ readonly EXECUTE: "flow:execute";
2266
+ /** Cancel running flows */
2267
+ readonly CANCEL: "flow:cancel";
2268
+ /** Check flow status */
2269
+ readonly STATUS: "flow:status";
2270
+ };
2271
+ /**
2272
+ * Upload permissions for file upload operations.
2273
+ */
2274
+ declare const UPLOAD_PERMISSIONS: {
2275
+ /** Full access to all upload operations */
2276
+ readonly ALL: "upload:*";
2277
+ /** Create uploads */
2278
+ readonly CREATE: "upload:create";
2279
+ /** Read upload status */
2280
+ readonly READ: "upload:read";
2281
+ /** Cancel uploads */
2282
+ readonly CANCEL: "upload:cancel";
2283
+ };
2284
+ /**
2285
+ * All available permissions organized by category.
2286
+ *
2287
+ * @example
2288
+ * ```typescript
2289
+ * import { PERMISSIONS } from "@uploadista/server";
2290
+ *
2291
+ * const adminPermissions = [PERMISSIONS.ENGINE.ALL];
2292
+ * const userPermissions = [PERMISSIONS.FLOW.ALL, PERMISSIONS.UPLOAD.ALL];
2293
+ * ```
2294
+ */
2295
+ declare const PERMISSIONS: {
2296
+ readonly ENGINE: {
2297
+ /** Full admin access to all engine operations */
2298
+ readonly ALL: "engine:*";
2299
+ /** Access health endpoint */
2300
+ readonly HEALTH: "engine:health";
2301
+ /** Access readiness endpoint */
2302
+ readonly READINESS: "engine:readiness";
2303
+ /** Access metrics endpoint */
2304
+ readonly METRICS: "engine:metrics";
2305
+ /** Full DLQ access (implies read and write) */
2306
+ readonly DLQ: "engine:dlq";
2307
+ /** Read DLQ entries */
2308
+ readonly DLQ_READ: "engine:dlq:read";
2309
+ /** Retry/delete DLQ entries */
2310
+ readonly DLQ_WRITE: "engine:dlq:write";
2311
+ };
2312
+ readonly FLOW: {
2313
+ /** Full access to all flow operations */
2314
+ readonly ALL: "flow:*";
2315
+ /** Execute flows */
2316
+ readonly EXECUTE: "flow:execute";
2317
+ /** Cancel running flows */
2318
+ readonly CANCEL: "flow:cancel";
2319
+ /** Check flow status */
2320
+ readonly STATUS: "flow:status";
2321
+ };
2322
+ readonly UPLOAD: {
2323
+ /** Full access to all upload operations */
2324
+ readonly ALL: "upload:*";
2325
+ /** Create uploads */
2326
+ readonly CREATE: "upload:create";
2327
+ /** Read upload status */
2328
+ readonly READ: "upload:read";
2329
+ /** Cancel uploads */
2330
+ readonly CANCEL: "upload:cancel";
2331
+ };
2332
+ };
2333
+ /** All engine permission strings */
2334
+ type EnginePermission = (typeof ENGINE_PERMISSIONS)[keyof typeof ENGINE_PERMISSIONS];
2335
+ /** All flow permission strings */
2336
+ type FlowPermission = (typeof FLOW_PERMISSIONS)[keyof typeof FLOW_PERMISSIONS];
2337
+ /** All upload permission strings */
2338
+ type UploadPermission = (typeof UPLOAD_PERMISSIONS)[keyof typeof UPLOAD_PERMISSIONS];
2339
+ /**
2340
+ * Union type of all valid permission strings.
2341
+ * Includes standard permissions and allows custom permissions via string.
2342
+ */
2343
+ type Permission = EnginePermission | FlowPermission | UploadPermission | (string & {});
2344
+ /**
2345
+ * Predefined permission sets for common use cases.
2346
+ */
2347
+ declare const PERMISSION_SETS: {
2348
+ /** Full admin access - all engine, flow, and upload permissions */
2349
+ readonly ADMIN: readonly ["engine:*"];
2350
+ /** Organization owner - all flow and upload permissions */
2351
+ readonly ORGANIZATION_OWNER: readonly ["flow:*", "upload:*"];
2352
+ /** Organization member - same as owner for now */
2353
+ readonly ORGANIZATION_MEMBER: readonly ["flow:*", "upload:*"];
2354
+ /** API key - limited to execute flows and create uploads */
2355
+ readonly API_KEY: readonly ["flow:execute", "upload:create"];
2356
+ };
2357
+ /**
2358
+ * Hierarchical permission relationships.
2359
+ * When a parent permission is granted, all child permissions are implied.
2360
+ */
2361
+ declare const PERMISSION_HIERARCHY: Record<string, readonly string[]>;
2362
+ //#endregion
2363
+ //#region src/permissions/matcher.d.ts
2364
+ /**
2365
+ * Permission Matching Logic
2366
+ *
2367
+ * Implements permission matching with support for:
2368
+ * - Exact match: `engine:health` matches `engine:health`
2369
+ * - Wildcard match: `engine:*` matches `engine:health`, `engine:metrics`, etc.
2370
+ * - Hierarchical match: `engine:dlq` implies `engine:dlq:read` and `engine:dlq:write`
2371
+ */
2372
+ /**
2373
+ * Checks if a granted permission matches a required permission.
2374
+ *
2375
+ * @param granted - The permission that has been granted to the user
2376
+ * @param required - The permission that is required for the operation
2377
+ * @returns true if the granted permission satisfies the required permission
2378
+ *
2379
+ * @example
2380
+ * ```typescript
2381
+ * matchesPermission("engine:*", "engine:health") // true (wildcard)
2382
+ * matchesPermission("engine:health", "engine:health") // true (exact)
2383
+ * matchesPermission("engine:dlq", "engine:dlq:read") // true (hierarchical)
2384
+ * matchesPermission("flow:execute", "engine:health") // false
2385
+ * ```
2386
+ */
2387
+ declare const matchesPermission: (granted: string, required: string) => boolean;
2388
+ /**
2389
+ * Checks if any of the granted permissions satisfy the required permission.
2390
+ *
2391
+ * @param grantedPermissions - Array of permissions granted to the user
2392
+ * @param required - The permission that is required for the operation
2393
+ * @returns true if any granted permission satisfies the required permission
2394
+ *
2395
+ * @example
2396
+ * ```typescript
2397
+ * hasPermission(["flow:*", "upload:create"], "flow:execute") // true
2398
+ * hasPermission(["upload:create"], "flow:execute") // false
2399
+ * ```
2400
+ */
2401
+ declare const hasPermission: (grantedPermissions: readonly string[], required: string) => boolean;
2402
+ /**
2403
+ * Checks if any of the granted permissions satisfy any of the required permissions.
2404
+ *
2405
+ * @param grantedPermissions - Array of permissions granted to the user
2406
+ * @param requiredPermissions - Array of permissions, any of which would be sufficient
2407
+ * @returns true if any granted permission satisfies any required permission
2408
+ *
2409
+ * @example
2410
+ * ```typescript
2411
+ * hasAnyPermission(["upload:create"], ["flow:execute", "upload:create"]) // true
2412
+ * hasAnyPermission(["upload:read"], ["flow:execute", "upload:create"]) // false
2413
+ * ```
2414
+ */
2415
+ declare const hasAnyPermission: (grantedPermissions: readonly string[], requiredPermissions: readonly string[]) => boolean;
2416
+ /**
2417
+ * Checks if all of the required permissions are satisfied.
2418
+ *
2419
+ * @param grantedPermissions - Array of permissions granted to the user
2420
+ * @param requiredPermissions - Array of permissions, all of which must be satisfied
2421
+ * @returns true if all required permissions are satisfied
2422
+ *
2423
+ * @example
2424
+ * ```typescript
2425
+ * hasAllPermissions(["flow:*", "upload:*"], ["flow:execute", "upload:create"]) // true
2426
+ * hasAllPermissions(["flow:execute"], ["flow:execute", "upload:create"]) // false
2427
+ * ```
2428
+ */
2429
+ declare const hasAllPermissions: (grantedPermissions: readonly string[], requiredPermissions: readonly string[]) => boolean;
2430
+ /**
2431
+ * Expands a permission to include all implied permissions.
2432
+ * Useful for display or audit purposes.
2433
+ *
2434
+ * @param permission - The permission to expand
2435
+ * @returns Array of the permission and all implied permissions
2436
+ *
2437
+ * @example
2438
+ * ```typescript
2439
+ * expandPermission("engine:dlq") // ["engine:dlq", "engine:dlq:read", "engine:dlq:write"]
2440
+ * expandPermission("engine:health") // ["engine:health"]
2441
+ * ```
2442
+ */
2443
+ declare const expandPermission: (permission: string) => string[];
2444
+ //#endregion
2445
+ //#region src/permissions/errors.d.ts
2446
+ /**
2447
+ * Authorization error - indicates the user lacks required permissions.
2448
+ * Returns HTTP 403 Forbidden status.
2449
+ *
2450
+ * @example
2451
+ * ```typescript
2452
+ * if (!hasPermission(permissions, "engine:metrics")) {
2453
+ * throw new AuthorizationError("engine:metrics");
2454
+ * }
2455
+ * ```
2456
+ */
2457
+ declare class AuthorizationError extends AdapterError {
2458
+ /**
2459
+ * The permission that was required but not granted.
2460
+ */
2461
+ readonly requiredPermission: string;
2462
+ constructor(requiredPermission: string, message?: string);
2463
+ }
2464
+ /**
2465
+ * Authentication required error - indicates no authentication context.
2466
+ * Returns HTTP 401 Unauthorized status.
2467
+ *
2468
+ * @example
2469
+ * ```typescript
2470
+ * if (!authContext) {
2471
+ * throw new AuthenticationRequiredError();
2472
+ * }
2473
+ * ```
2474
+ */
2475
+ declare class AuthenticationRequiredError extends AdapterError {
2476
+ constructor(message?: string);
2477
+ }
2478
+ /**
2479
+ * Organization mismatch error - indicates accessing a resource from another organization.
2480
+ * Returns HTTP 403 Forbidden status.
2481
+ *
2482
+ * @example
2483
+ * ```typescript
2484
+ * if (resource.organizationId !== clientId) {
2485
+ * throw new OrganizationMismatchError();
2486
+ * }
2487
+ * ```
2488
+ */
2489
+ declare class OrganizationMismatchError extends AdapterError {
2490
+ constructor(message?: string);
2491
+ }
2492
+ /**
2493
+ * Quota exceeded error - indicates usage quota has been exceeded.
2494
+ * Returns HTTP 402 Payment Required status.
2495
+ *
2496
+ * @example
2497
+ * ```typescript
2498
+ * if (usage > quota) {
2499
+ * throw new QuotaExceededError("Storage quota exceeded");
2500
+ * }
2501
+ * ```
2502
+ */
2503
+ declare class QuotaExceededError extends AdapterError {
2504
+ constructor(message?: string, code?: string);
2505
+ }
2506
+ /**
2507
+ * Creates a standardized error response body for AuthorizationError.
2508
+ * Includes the required permission in the response.
2509
+ *
2510
+ * @param error - The AuthorizationError to format
2511
+ * @returns Standardized error response body
2512
+ */
2513
+ declare const createAuthorizationErrorResponseBody: (error: AuthorizationError) => {
2514
+ error: string;
2515
+ code: string;
2516
+ requiredPermission: string;
2517
+ timestamp: string;
2518
+ };
2519
+ //#endregion
2000
2520
  //#region src/plugins-typing.d.ts
2001
2521
  /**
2002
2522
  * @deprecated Use `ExtractLayerServices` from `@uploadista/core/flow/types` instead.
@@ -2090,9 +2610,56 @@ declare const AuthContextService_base: Context.TagClass<AuthContextService, "Aut
2090
2610
  readonly getMetadata: () => Effect.Effect<Record<string, unknown>>;
2091
2611
  /**
2092
2612
  * Check if the current client has a specific permission.
2613
+ * Supports exact match, wildcard match, and hierarchical match.
2093
2614
  * Returns false if no authentication context or permission not found.
2615
+ *
2616
+ * @example
2617
+ * ```typescript
2618
+ * // Exact match
2619
+ * yield* authService.hasPermission("engine:health")
2620
+ *
2621
+ * // Wildcard: user with "engine:*" will match "engine:health"
2622
+ * yield* authService.hasPermission("engine:health")
2623
+ *
2624
+ * // Hierarchical: user with "engine:dlq" will match "engine:dlq:read"
2625
+ * yield* authService.hasPermission("engine:dlq:read")
2626
+ * ```
2094
2627
  */
2095
2628
  readonly hasPermission: (permission: string) => Effect.Effect<boolean>;
2629
+ /**
2630
+ * Check if the current client has any of the specified permissions.
2631
+ * Returns true if at least one permission is granted.
2632
+ */
2633
+ readonly hasAnyPermission: (permissions: readonly string[]) => Effect.Effect<boolean>;
2634
+ /**
2635
+ * Require a specific permission, failing with AuthorizationError if not granted.
2636
+ * Use this when you want to fail fast on missing permissions.
2637
+ *
2638
+ * @throws AuthorizationError if permission is not granted
2639
+ * @throws AuthenticationRequiredError if no auth context
2640
+ *
2641
+ * @example
2642
+ * ```typescript
2643
+ * const protectedHandler = Effect.gen(function* () {
2644
+ * const authService = yield* AuthContextService;
2645
+ * yield* authService.requirePermission("engine:metrics");
2646
+ * // Only reaches here if permission is granted
2647
+ * return yield* getMetrics();
2648
+ * });
2649
+ * ```
2650
+ */
2651
+ readonly requirePermission: (permission: string) => Effect.Effect<void, AuthorizationError | AuthenticationRequiredError>;
2652
+ /**
2653
+ * Require authentication, failing with AuthenticationRequiredError if not authenticated.
2654
+ *
2655
+ * @throws AuthenticationRequiredError if no auth context
2656
+ */
2657
+ readonly requireAuthentication: () => Effect.Effect<AuthContext, AuthenticationRequiredError>;
2658
+ /**
2659
+ * Get all permissions granted to the current client.
2660
+ * Returns empty array if no authentication context or no permissions.
2661
+ */
2662
+ readonly getPermissions: () => Effect.Effect<readonly string[]>;
2096
2663
  /**
2097
2664
  * Get the full authentication context if available.
2098
2665
  * Returns null if no authentication context is available.
@@ -2121,14 +2688,26 @@ declare const AuthContextService_base: Context.TagClass<AuthContextService, "Aut
2121
2688
  * ```
2122
2689
  */
2123
2690
  declare class AuthContextService extends AuthContextService_base {}
2691
+ /**
2692
+ * Options for creating an AuthContextService Layer.
2693
+ */
2694
+ interface AuthContextServiceOptions {
2695
+ /**
2696
+ * When true, bypasses all permission checks (grants all permissions).
2697
+ * Used when no auth middleware is configured (backward compatibility).
2698
+ * @default false
2699
+ */
2700
+ bypassAuth?: boolean;
2701
+ }
2124
2702
  /**
2125
2703
  * Creates an AuthContextService Layer from an AuthContext.
2126
2704
  * This is typically called by adapters after successful authentication.
2127
2705
  *
2128
2706
  * @param authContext - The authentication context from middleware
2707
+ * @param options - Optional configuration for auth behavior
2129
2708
  * @returns Effect Layer providing AuthContextService
2130
2709
  */
2131
- declare const AuthContextServiceLive: (authContext: AuthContext | null) => Layer.Layer<AuthContextService>;
2710
+ declare const AuthContextServiceLive: (authContext: AuthContext | null, options?: AuthContextServiceOptions) => Layer.Layer<AuthContextService>;
2132
2711
  /**
2133
2712
  * No-auth implementation of AuthContextService.
2134
2713
  * Returns null/empty values for all operations.
@@ -2136,5 +2715,49 @@ declare const AuthContextServiceLive: (authContext: AuthContext | null) => Layer
2136
2715
  */
2137
2716
  declare const NoAuthContextServiceLive: Layer.Layer<AuthContextService>;
2138
2717
  //#endregion
2139
- export { AdapterError, AuthCacheConfig, AuthCacheService, AuthCacheServiceLive, AuthContext, AuthContextService, AuthContextServiceLive, AuthCredentialsResponse, AuthFailedEvent, AuthResult, BadRequestError, BadRequestRequest, BadRequestResponse, CancelFlowRequest, CancelFlowResponse, ConnectionEvent, CreateUploadRequest, CreateUploadResponse, DlqCleanupRequest, DlqCleanupResponse, DlqDeleteRequest, DlqDeleteResponse, DlqGetRequest, DlqGetResponse, DlqListRequest, DlqListResponse, DlqResolveRequest, DlqResolveResponse, DlqRetryAllRequest, DlqRetryAllResponse, DlqRetryRequest, DlqRetryResponse, DlqStatsRequest, DlqStatsResponse, ErrorEvent, ExtractFlowPluginRequirements, ExtractServicesFromLayers, FlowEventMessage, FlowRequirementsOf, FlowServerLayerConfig, FlowSuccess, FlowsFunction, GetCapabilitiesRequest, GetCapabilitiesResponse, GetFlowRequest, GetFlowResponse, GetJobStatusRequest, GetJobStatusResponse, GetUploadRequest, GetUploadResponse, HealthComponentsRequest, HealthComponentsResponse, HealthReadyRequest, HealthReadyResponse, HealthRequest, HealthResponse, InferFlowRequirements, InvalidPathEvent, KnownPluginLayer, LayerSuccessUnion, MethodNotAllowedRequest, MethodNotAllowedResponse, NoAuthCacheServiceLive, NoAuthContextServiceLive, NotFoundError, NotFoundRequest, NotFoundResponse, PauseFlowRequest, PauseFlowResponse, PingEvent, PluginAssertion, PluginServices, PluginTuple, PluginValidationResult, RequiredPluginsOf, ResumeFlowRequest, ResumeFlowResponse, RunFlowRequest, RunFlowResponse, RuntimePluginLayers, ServerAdapter, StandardRequest, StandardResponse, SubscribeFlowEvent, SubscribeUploadEvent, TypeSafeFlowFunction, TypeSafePluginConfig, TypeSafeServerConfig, UnsubscribeFlowEvent, UnsubscribeUploadEvent, UnsupportedContentTypeRequest, UnsupportedContentTypeResponse, UploadChunkRequest, UploadChunkResponse, UploadEventMessage, UploadServerLayerConfig, UploadistaRequest, UploadistaResponse, UploadistaRoute, UploadistaRouteType, UploadistaServer, UploadistaServerConfig, UploadistaStandardResponse, ValidatePlugins, ValidationError, type WebSocketConnection, type WebSocketConnectionRequest, WebSocketEvent, WebSocketEventType, WebSocketHandler, WebSocketIncomingEvent, WebSocketOutgoingEvent, createErrorResponseBody, createFlowServerLayer, createGenericErrorResponseBody, createTypeSafeServer, createUploadServerLayer, createUploadistaErrorResponseBody, createUploadistaServer, defineFlow, defineSimpleFlow, extractFlowAndStorageId, extractJobAndNodeId, extractJobIdFromStatus, extractServiceIdentifiers, formatPluginValidationError, getAuthCredentials, getLastSegment, getRouteSegments, handleFlowError, handleWebSocketClose, handleWebSocketError, handleWebSocketMessage, handleWebSocketOpen, hasBasePath, parseUrlSegments, validatePluginRequirements, validatePluginRequirementsEffect, validatePluginsOrThrow };
2718
+ //#region src/usage-hooks/service.d.ts
2719
+ declare const UsageHookService_base: Context.TagClass<UsageHookService, "UsageHookService", {
2720
+ /**
2721
+ * Execute onUploadStart hook if configured.
2722
+ * Returns continue result if no hook is configured or on error/timeout.
2723
+ */
2724
+ readonly onUploadStart: (ctx: UploadUsageContext) => Effect.Effect<UsageHookResult>;
2725
+ /**
2726
+ * Execute onUploadComplete hook if configured.
2727
+ * Errors are logged but swallowed (fire-and-forget).
2728
+ */
2729
+ readonly onUploadComplete: (ctx: UploadUsageContext) => Effect.Effect<void>;
2730
+ /**
2731
+ * Execute onFlowStart hook if configured.
2732
+ * Returns continue result if no hook is configured or on error/timeout.
2733
+ */
2734
+ readonly onFlowStart: (ctx: FlowUsageContext) => Effect.Effect<UsageHookResult>;
2735
+ /**
2736
+ * Execute onFlowComplete hook if configured.
2737
+ * Errors are logged but swallowed (fire-and-forget).
2738
+ */
2739
+ readonly onFlowComplete: (ctx: FlowUsageContext) => Effect.Effect<void>;
2740
+ }>;
2741
+ /**
2742
+ * Usage Hook Service
2743
+ *
2744
+ * Provides methods to execute usage hooks during upload and flow processing.
2745
+ * Handles timeout and error recovery gracefully.
2746
+ */
2747
+ declare class UsageHookService extends UsageHookService_base {}
2748
+ /**
2749
+ * Creates a UsageHookService Layer from configuration.
2750
+ *
2751
+ * @param config - Usage hook configuration with optional hooks and timeout
2752
+ * @returns Effect Layer providing UsageHookService
2753
+ */
2754
+ declare const UsageHookServiceLive: (config?: UsageHookConfig) => Layer.Layer<UsageHookService>;
2755
+ /**
2756
+ * No-op implementation of UsageHookService.
2757
+ * All hooks are no-ops that return continue/void.
2758
+ * Used when no usage hooks are configured (default backward compatibility).
2759
+ */
2760
+ declare const NoUsageHookServiceLive: Layer.Layer<UsageHookService>;
2761
+ //#endregion
2762
+ export { AdapterError, AuthCacheConfig, AuthCacheService, AuthCacheServiceLive, AuthContext, AuthContextService, AuthContextServiceLive, AuthContextServiceOptions, AuthCredentialsResponse, AuthFailedEvent, AuthResult, AuthenticationRequiredError, AuthorizationError, BadRequestError, BadRequestRequest, BadRequestResponse, BaseUsageMetadata, CancelFlowRequest, CancelFlowResponse, ConnectionEvent, CreateUploadRequest, CreateUploadResponse, DEFAULT_USAGE_HOOK_TIMEOUT, DlqCleanupRequest, DlqCleanupResponse, DlqDeleteRequest, DlqDeleteResponse, DlqGetRequest, DlqGetResponse, DlqListRequest, DlqListResponse, DlqResolveRequest, DlqResolveResponse, DlqRetryAllRequest, DlqRetryAllResponse, DlqRetryRequest, DlqRetryResponse, DlqStatsRequest, DlqStatsResponse, ENGINE_PERMISSIONS, EnginePermission, ErrorEvent, ExtractFlowPluginRequirements, ExtractServicesFromLayers, FLOW_PERMISSIONS, FlowEventMessage, FlowPermission, FlowRequirementsOf, FlowServerLayerConfig, FlowSuccess, FlowUsageContext, FlowUsageMetadata, FlowsFunction, GetCapabilitiesRequest, GetCapabilitiesResponse, GetFlowRequest, GetFlowResponse, GetJobStatusRequest, GetJobStatusResponse, GetUploadRequest, GetUploadResponse, HealthComponentsRequest, HealthComponentsResponse, HealthReadyRequest, HealthReadyResponse, HealthRequest, HealthResponse, InferFlowRequirements, InvalidPathEvent, KnownPluginLayer, LayerSuccessUnion, MethodNotAllowedRequest, MethodNotAllowedResponse, NoAuthCacheServiceLive, NoAuthContextServiceLive, NoUsageHookServiceLive, NotFoundError, NotFoundRequest, NotFoundResponse, OnFlowCompleteHook, OnFlowStartHook, OnUploadCompleteHook, OnUploadStartHook, OrganizationMismatchError, PERMISSIONS, PERMISSION_HIERARCHY, PERMISSION_SETS, PauseFlowRequest, PauseFlowResponse, Permission, PingEvent, PluginAssertion, PluginServices, PluginTuple, PluginValidationResult, QuotaExceededError, RequiredPluginsOf, ResumeFlowRequest, ResumeFlowResponse, RunFlowRequest, RunFlowResponse, RuntimePluginLayers, ServerAdapter, StandardRequest, StandardResponse, SubscribeFlowEvent, SubscribeUploadEvent, TypeSafeFlowFunction, TypeSafePluginConfig, TypeSafeServerConfig, UPLOAD_PERMISSIONS, UnsubscribeFlowEvent, UnsubscribeUploadEvent, UnsupportedContentTypeRequest, UnsupportedContentTypeResponse, UploadChunkRequest, UploadChunkResponse, UploadEventMessage, UploadPermission, UploadServerLayerConfig, UploadUsageContext, UploadUsageMetadata, UploadistaRequest, UploadistaResponse, UploadistaRoute, UploadistaRouteType, UploadistaServer, UploadistaServerConfig, UploadistaStandardResponse, UsageContext, UsageHookConfig, UsageHookResult, UsageHookService, UsageHookServiceLive, UsageHooks, ValidatePlugins, ValidationError, type WebSocketConnection, type WebSocketConnectionRequest, WebSocketEvent, WebSocketEventType, WebSocketHandler, WebSocketIncomingEvent, WebSocketOutgoingEvent, abortResult, continueResult, createAuthorizationErrorResponseBody, createErrorResponseBody, createFlowServerLayer, createGenericErrorResponseBody, createTypeSafeServer, createUploadServerLayer, createUploadistaErrorResponseBody, createUploadistaServer, defineFlow, defineSimpleFlow, expandPermission, extractFlowAndStorageId, extractJobAndNodeId, extractJobIdFromStatus, extractServiceIdentifiers, formatPluginValidationError, getAuthCredentials, getLastSegment, getRouteSegments, handleFlowError, handleWebSocketClose, handleWebSocketError, handleWebSocketMessage, handleWebSocketOpen, hasAllPermissions, hasAnyPermission, hasBasePath, hasPermission, matchesPermission, parseUrlSegments, validatePluginRequirements, validatePluginRequirementsEffect, validatePluginsOrThrow };
2140
2763
  //# sourceMappingURL=index.d.cts.map