alepha 0.9.4 → 0.10.0

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/server.d.ts CHANGED
@@ -1,13 +1,13 @@
1
- import * as _alepha_core1 from "alepha";
2
- import { Alepha, AlephaError, Async, Descriptor, FileLike, KIND, Static, StreamLike, TObject, TSchema } from "alepha";
3
- import * as _alepha_logger0 from "alepha/logger";
1
+ import * as _alepha_core11 from "alepha";
2
+ import { Alepha, AlephaError, Async, Descriptor, FileLike, KIND, Static, StreamLike, TArray, TFile, TObject, TRecord, TSchema, TStream, TString, TVoid } from "alepha";
3
+ import * as _alepha_logger3 from "alepha/logger";
4
4
  import { Readable } from "node:stream";
5
5
  import { ReadableStream } from "node:stream/web";
6
6
  import { Route, RouterProvider } from "alepha/router";
7
7
  import * as _alepha_cache0 from "alepha/cache";
8
8
  import { IncomingMessage, ServerResponse as ServerResponse$1 } from "node:http";
9
9
  import { DateTimeProvider, DurationLike } from "alepha/datetime";
10
- import * as _sinclair_typebox7 from "@sinclair/typebox";
10
+ import * as typebox0 from "typebox";
11
11
  import * as http0 from "http";
12
12
 
13
13
  //#region src/constants/routeMethods.d.ts
@@ -15,15 +15,31 @@ declare const routeMethods: readonly ["GET", "POST", "PUT", "PATCH", "DELETE", "
15
15
  type RouteMethod = (typeof routeMethods)[number];
16
16
  //#endregion
17
17
  //#region src/helpers/ServerReply.d.ts
18
+ /**
19
+ * Helper for building server replies.
20
+ */
18
21
  declare class ServerReply {
19
22
  headers: Record<string, string> & {
20
23
  "set-cookie"?: string[];
21
24
  };
22
25
  status?: number;
23
26
  body?: any;
27
+ /**
28
+ * Redirect to a given URL with optional status code (default 302).
29
+ */
24
30
  redirect(url: string, status?: number): void;
25
- setStatus(status: number): void;
26
- setHeader(name: string, value: string): void;
31
+ /**
32
+ * Set the response status code.
33
+ */
34
+ setStatus(status: number): this;
35
+ /**
36
+ * Set a response header.
37
+ */
38
+ setHeader(name: string, value: string): this;
39
+ /**
40
+ * Set the response body.
41
+ */
42
+ setBody(body: any): this;
27
43
  }
28
44
  //#endregion
29
45
  //#region src/services/UserAgentParser.d.ts
@@ -43,15 +59,17 @@ declare class UserAgentParser {
43
59
  }
44
60
  //#endregion
45
61
  //#region src/interfaces/ServerRequest.d.ts
62
+ type TRequestBody = TObject | TString | TArray | TRecord | TStream;
63
+ type TResponseBody = TObject | TString | TRecord | TFile | TArray | TStream | TVoid;
46
64
  interface RequestConfigSchema {
47
- body?: TSchema;
65
+ body?: TRequestBody;
48
66
  params?: TObject;
49
67
  query?: TObject;
50
68
  headers?: TObject;
51
- response?: TSchema;
69
+ response?: TResponseBody;
52
70
  }
53
71
  interface ServerRequestConfig<TConfig extends RequestConfigSchema = RequestConfigSchema> {
54
- body: TConfig["body"] extends TSchema ? Static<TConfig["body"]> : any;
72
+ body: TConfig["body"] extends TRequestBody ? Static<TConfig["body"]> : any;
55
73
  headers: TConfig["headers"] extends TObject ? Static<TConfig["headers"]> : Record<string, string>;
56
74
  params: TConfig["params"] extends TObject ? Static<TConfig["params"]> : Record<string, string>;
57
75
  query: TConfig["query"] extends TObject ? Static<TConfig["query"]> : Record<string, string>;
@@ -60,7 +78,16 @@ type ServerRequestConfigEntry<TConfig extends RequestConfigSchema = RequestConfi
60
78
  interface ServerRequest<TConfig extends RequestConfigSchema = RequestConfigSchema> extends ServerRequestConfig<TConfig> {
61
79
  method: RouteMethod;
62
80
  url: URL;
81
+ requestId: string;
82
+ /**
83
+ * Client IP address.
84
+ * Will parse `X-Forwarded-For` header if present.
85
+ */
63
86
  ip?: string;
87
+ /**
88
+ * Value of the `Host` header sent by the client.
89
+ */
90
+ host?: string;
64
91
  /**
65
92
  * Browser user agent information.
66
93
  * Information are not guaranteed to be accurate. Use with caution.
@@ -69,10 +96,26 @@ interface ServerRequest<TConfig extends RequestConfigSchema = RequestConfigSchem
69
96
  */
70
97
  userAgent: UserAgentInfo;
71
98
  metadata: Record<string, any>;
99
+ /**
100
+ * Reply object to be used to send response.
101
+ */
72
102
  reply: ServerReply;
103
+ /**
104
+ * Raw request and response objects from platform.
105
+ * You should avoid using this property as much as possible to keep your code platform-agnostic.
106
+ */
73
107
  raw: {
108
+ /**
109
+ * Node.js request and response objects.
110
+ */
74
111
  node?: {
112
+ /**
113
+ * Node.js IncomingMessage object. (request)
114
+ */
75
115
  req: IncomingMessage;
116
+ /**
117
+ * Node.js ServerResponse object. (response)
118
+ */
76
119
  res: ServerResponse$1;
77
120
  };
78
121
  };
@@ -86,18 +129,18 @@ interface ServerRoute<TConfig extends RequestConfigSchema = RequestConfigSchema>
86
129
  */
87
130
  silent?: boolean;
88
131
  }
89
- type ServerResponseBody<TConfig extends RequestConfigSchema = RequestConfigSchema> = TConfig["response"] extends TSchema ? Static<TConfig["response"]> : ResponseBodyType;
132
+ type ServerResponseBody<TConfig extends RequestConfigSchema = RequestConfigSchema> = TConfig["response"] extends TResponseBody ? Static<TConfig["response"]> : ResponseBodyType;
90
133
  type ResponseKind = "json" | "text" | "void" | "file" | "any";
91
134
  type ResponseBodyType = string | Buffer | StreamLike | undefined | null | void;
92
135
  type ServerHandler<TConfig extends RequestConfigSchema = RequestConfigSchema> = (request: ServerRequest<TConfig>) => Async<ServerResponseBody<TConfig>>;
93
- type ServerMiddlewareHandler<TConfig extends RequestConfigSchema = RequestConfigSchema> = (request: ServerRequest<TConfig>) => Async<ServerResponseBody<TConfig> | undefined>;
94
136
  interface ServerResponse {
95
137
  body: string | Buffer | ArrayBuffer | Readable | ReadableStream;
96
138
  headers: Record<string, string>;
97
139
  status: number;
98
140
  }
141
+ type ServerRouteRequestHandler = (request: ServerRawRequest) => Promise<ServerResponse>;
99
142
  interface ServerRouteMatcher extends Route {
100
- handler: (request: ServerRawRequest) => Promise<ServerResponse>;
143
+ handler: ServerRouteRequestHandler;
101
144
  }
102
145
  interface ServerRawRequest {
103
146
  method: RouteMethod;
@@ -120,13 +163,26 @@ declare abstract class ServerProvider {
120
163
  protected isViteNotFound(url?: string, route?: Route, params?: Record<string, string>): boolean;
121
164
  }
122
165
  //#endregion
166
+ //#region src/services/ServerRequestParser.d.ts
167
+ declare class ServerRequestParser {
168
+ protected readonly alepha: Alepha;
169
+ protected readonly userAgentParser: UserAgentParser;
170
+ createServerRequest(rawRequest: ServerRawRequest): ServerRequest;
171
+ getRequestId(request: ServerRawRequest): string | undefined;
172
+ getRequestUserAgent(request: ServerRawRequest): UserAgentInfo;
173
+ getRequestIp(request: ServerRawRequest): string | undefined;
174
+ }
175
+ //#endregion
123
176
  //#region src/providers/ServerTimingProvider.d.ts
124
177
  type TimingMap = Record<string, [number, number]>;
125
178
  declare class ServerTimingProvider {
126
- protected readonly log: _alepha_logger0.Logger;
179
+ protected readonly log: _alepha_logger3.Logger;
127
180
  protected readonly alepha: Alepha;
128
- readonly onRequest: _alepha_core1.HookDescriptor<"server:onRequest">;
129
- readonly onResponse: _alepha_core1.HookDescriptor<"server:onResponse">;
181
+ options: {
182
+ disabled: boolean;
183
+ };
184
+ readonly onRequest: _alepha_core11.HookDescriptor<"server:onRequest">;
185
+ readonly onResponse: _alepha_core11.HookDescriptor<"server:onResponse">;
130
186
  protected get handlerName(): string;
131
187
  beginTiming(name: string): void;
132
188
  endTiming(name: string): void;
@@ -145,11 +201,9 @@ declare class ServerRouterProvider extends RouterProvider<ServerRouteMatcher> {
145
201
  protected readonly alepha: Alepha;
146
202
  protected readonly routes: ServerRoute[];
147
203
  protected readonly serverTimingProvider: ServerTimingProvider;
148
- protected readonly userAgentParser: UserAgentParser;
204
+ protected readonly serverRequestParser: ServerRequestParser;
149
205
  getRoutes(): ServerRoute[];
150
206
  createRoute<TConfig extends RequestConfigSchema = RequestConfigSchema>(route: ServerRoute<TConfig>): void;
151
- onRequest(route: ServerRoute, rawRequest: ServerRawRequest, responseKind: ResponseKind): Promise<ServerResponse>;
152
- protected getClientIp(request: ServerRawRequest): string | undefined;
153
207
  protected processRequest(request: ServerRequest, route: ServerRoute, responseKind: ResponseKind): Promise<{
154
208
  status: number;
155
209
  headers: Record<string, string> & {
@@ -168,16 +222,14 @@ declare class ServerRouterProvider extends RouterProvider<ServerRouteMatcher> {
168
222
  //#endregion
169
223
  //#region src/services/HttpClient.d.ts
170
224
  declare class HttpClient {
171
- protected readonly log: _alepha_logger0.Logger;
225
+ protected readonly log: _alepha_logger3.Logger;
172
226
  protected readonly alepha: Alepha;
173
227
  readonly cache: _alepha_cache0.CacheDescriptorFn<HttpClientCache, any[]>;
174
228
  protected readonly pendingRequests: HttpClientPendingRequests;
175
- clear(): Promise<void>;
176
229
  fetchAction(args: FetchActionArgs): Promise<FetchResponse>;
177
230
  fetch<T>(url: string, request?: RequestInit,
178
231
  // standard options
179
232
  options?: FetchOptions): Promise<FetchResponse<T>>;
180
- json<T = any>(url: string, options?: RequestInit): Promise<T>;
181
233
  protected url(host: string, action: HttpAction, args: ServerRequestConfigEntry): string;
182
234
  protected body(init: RequestInit, headers: Record<string, string>, action: HttpAction, args?: ServerRequestConfigEntry): Promise<void>;
183
235
  protected responseData(response: Response, options: FetchOptions): Promise<any>;
@@ -234,28 +286,547 @@ interface HttpAction {
234
286
  schema?: {
235
287
  params?: TObject;
236
288
  query?: TObject;
237
- body?: TSchema;
238
- response?: TSchema;
289
+ body?: TRequestBody;
290
+ response?: TResponseBody;
239
291
  };
240
292
  }
241
293
  //#endregion
242
294
  //#region src/descriptors/$action.d.ts
243
295
  /**
244
- * Create an action endpoint.
296
+ * Creates a server action descriptor for defining type-safe HTTP endpoints.
297
+ *
298
+ * Server actions are the core building blocks for REST APIs in the Alepha framework. They provide
299
+ * a declarative way to define HTTP endpoints with full TypeScript type safety, automatic schema
300
+ * validation, and integrated security features. Actions automatically handle routing, request
301
+ * parsing, response serialization, and OpenAPI documentation generation.
302
+ *
303
+ * **Key Features**
304
+ *
305
+ * - **Type Safety**: Full TypeScript inference for request/response types
306
+ * - **Schema Validation**: Automatic validation using TypeBox schemas
307
+ * - **Auto-routing**: Convention-based URL generation with customizable paths
308
+ * - **Multiple Invocation**: Call directly (`run()`) or via HTTP (`fetch()`)
309
+ * - **OpenAPI Integration**: Automatic documentation generation
310
+ * - **Security Integration**: Built-in authentication and authorization support
311
+ * - **Content Type Detection**: Automatic handling of JSON, form-data, and plain text
312
+ *
313
+ * **URL Generation**
314
+ *
315
+ * By default, actions are prefixed with `/api` (configurable via `SERVER_API_PREFIX`):
316
+ * - Property name becomes the endpoint path
317
+ * - Path parameters are automatically detected from schema
318
+ * - HTTP method defaults to GET, or POST if body schema is provided
319
+ *
320
+ * **Use Cases**
321
+ *
322
+ * Perfect for building robust REST APIs:
323
+ * - CRUD operations with full type safety
324
+ * - File upload and download endpoints
325
+ * - Real-time data processing APIs
326
+ * - Integration with external services
327
+ * - Microservice communication
328
+ * - Admin and management interfaces
329
+ *
330
+ * @example
331
+ * **Basic CRUD operations:**
332
+ * ```ts
333
+ * import { $action } from "alepha/server";
334
+ * import { t } from "alepha";
335
+ *
336
+ * class UserController {
337
+ * // GET /api/users
338
+ * getUsers = $action({
339
+ * description: "Retrieve all users with pagination",
340
+ * schema: {
341
+ * query: t.object({
342
+ * page: t.optional(t.number({ default: 1 })),
343
+ * limit: t.optional(t.number({ default: 10, maximum: 100 })),
344
+ * search: t.optional(t.string())
345
+ * }),
346
+ * response: t.object({
347
+ * users: t.array(t.object({
348
+ * id: t.string(),
349
+ * name: t.string(),
350
+ * email: t.string(),
351
+ * createdAt: t.datetime()
352
+ * })),
353
+ * total: t.number(),
354
+ * hasMore: t.boolean()
355
+ * })
356
+ * },
357
+ * handler: async ({ query }) => {
358
+ * const { page, limit, search } = query;
359
+ * const users = await this.userService.findUsers({ page, limit, search });
360
+ *
361
+ * return {
362
+ * users: users.items,
363
+ * total: users.total,
364
+ * hasMore: (page * limit) < users.total
365
+ * };
366
+ * }
367
+ * });
368
+ *
369
+ * // POST /api/users
370
+ * createUser = $action({
371
+ * description: "Create a new user account",
372
+ * schema: {
373
+ * body: t.object({
374
+ * name: t.string({ minLength: 2, maxLength: 100 }),
375
+ * email: t.string({ format: "email" }),
376
+ * password: t.string({ minLength: 8 }),
377
+ * role: t.optional(t.enum(["user", "admin"]))
378
+ * }),
379
+ * response: t.object({
380
+ * id: t.string(),
381
+ * name: t.string(),
382
+ * email: t.string(),
383
+ * role: t.string(),
384
+ * createdAt: t.datetime()
385
+ * })
386
+ * },
387
+ * handler: async ({ body }) => {
388
+ * // Password validation and hashing
389
+ * await this.authService.validatePassword(body.password);
390
+ * const hashedPassword = await this.authService.hashPassword(body.password);
391
+ *
392
+ * // Create user with default role
393
+ * const user = await this.userService.create({
394
+ * ...body,
395
+ * password: hashedPassword,
396
+ * role: body.role || "user"
397
+ * });
398
+ *
399
+ * // Return user without password
400
+ * const { password, ...publicUser } = user;
401
+ * return publicUser;
402
+ * }
403
+ * });
404
+ *
405
+ * // GET /api/users/:id
406
+ * getUser = $action({
407
+ * description: "Retrieve user by ID",
408
+ * schema: {
409
+ * params: t.object({
410
+ * id: t.string()
411
+ * }),
412
+ * response: t.object({
413
+ * id: t.string(),
414
+ * name: t.string(),
415
+ * email: t.string(),
416
+ * role: t.string(),
417
+ * profile: t.optional(t.object({
418
+ * bio: t.string(),
419
+ * avatar: t.string({ format: "uri" }),
420
+ * location: t.string()
421
+ * }))
422
+ * })
423
+ * },
424
+ * handler: async ({ params }) => {
425
+ * const user = await this.userService.findById(params.id);
426
+ * if (!user) {
427
+ * throw new Error(`User not found: ${params.id}`);
428
+ * }
429
+ * return user;
430
+ * }
431
+ * });
432
+ *
433
+ * // PUT /api/users/:id
434
+ * updateUser = $action({
435
+ * method: "PUT",
436
+ * description: "Update user information",
437
+ * schema: {
438
+ * params: t.object({ id: t.string() }),
439
+ * body: t.object({
440
+ * name: t.optional(t.string({ minLength: 2 })),
441
+ * email: t.optional(t.string({ format: "email" })),
442
+ * profile: t.optional(t.object({
443
+ * bio: t.optional(t.string()),
444
+ * avatar: t.optional(t.string({ format: "uri" })),
445
+ * location: t.optional(t.string())
446
+ * }))
447
+ * }),
448
+ * response: t.object({
449
+ * id: t.string(),
450
+ * name: t.string(),
451
+ * email: t.string(),
452
+ * updatedAt: t.datetime()
453
+ * })
454
+ * },
455
+ * handler: async ({ params, body }) => {
456
+ * const updatedUser = await this.userService.update(params.id, body);
457
+ * return updatedUser;
458
+ * }
459
+ * });
460
+ * }
461
+ * ```
462
+ *
463
+ * @example
464
+ * **File upload with multipart form data:**
465
+ * ```ts
466
+ * class FileController {
467
+ * uploadAvatar = $action({
468
+ * method: "POST",
469
+ * description: "Upload user avatar image",
470
+ * schema: {
471
+ * body: t.object({
472
+ * file: t.file({
473
+ * maxSize: 5 * 1024 * 1024, // 5MB
474
+ * allowedMimeTypes: ["image/jpeg", "image/png", "image/webp"]
475
+ * }),
476
+ * userId: t.string()
477
+ * }),
478
+ * response: t.object({
479
+ * url: t.string({ format: "uri" }),
480
+ * size: t.number(),
481
+ * mimeType: t.string(),
482
+ * uploadedAt: t.datetime()
483
+ * })
484
+ * },
485
+ * handler: async ({ body }) => {
486
+ * const { file, userId } = body;
487
+ *
488
+ * // Validate file
489
+ * await this.fileService.validateImage(file);
490
+ *
491
+ * // Generate unique filename
492
+ * const filename = `avatars/${userId}/${Date.now()}-${file.name}`;
493
+ *
494
+ * // Upload to storage
495
+ * const uploadResult = await this.storageService.upload(filename, file);
496
+ *
497
+ * // Update user profile
498
+ * await this.userService.updateAvatar(userId, uploadResult.url);
245
499
  *
246
- * By default, all actions are prefixed by `/api`.
247
- * If `name` is not provided, the action will be named after the property key.
248
- * If `path` is not provided, the action will be named after the function name.
500
+ * return {
501
+ * url: uploadResult.url,
502
+ * size: file.size,
503
+ * mimeType: file.type,
504
+ * uploadedAt: new Date().toISOString()
505
+ * };
506
+ * }
507
+ * });
508
+ *
509
+ * downloadFile = $action({
510
+ * method: "GET",
511
+ * description: "Download file by ID",
512
+ * schema: {
513
+ * params: t.object({ id: t.string() }),
514
+ * query: t.object({
515
+ * download: t.optional(t.boolean()),
516
+ * thumbnail: t.optional(t.boolean())
517
+ * }),
518
+ * response: t.file()
519
+ * },
520
+ * handler: async ({ params, query, reply, user }) => {
521
+ * const file = await this.fileService.findById(params.id);
522
+ * if (!file) {
523
+ * throw new Error("File not found");
524
+ * }
525
+ *
526
+ * // Check permissions
527
+ * await this.fileService.checkAccess(params.id, user.id);
528
+ *
529
+ * const fileBuffer = query.thumbnail
530
+ * ? await this.fileService.getThumbnail(file.id)
531
+ * : await this.fileService.getBuffer(file.path);
532
+ *
533
+ * // Set appropriate headers
534
+ * reply.header("Content-Type", file.mimeType);
535
+ * reply.header("Content-Length", fileBuffer.length);
536
+ *
537
+ * if (query.download) {
538
+ * reply.header("Content-Disposition", `attachment; filename="${file.name}"`);
539
+ * }
540
+ *
541
+ * return fileBuffer;
542
+ * }
543
+ * });
544
+ * }
545
+ * ```
249
546
  *
250
547
  * @example
548
+ * **Advanced API with custom paths and grouped operations:**
251
549
  * ```ts
252
- * class MyController {
253
- * hello = $action({
254
- * handler: () => "Hello World",
255
- * })
550
+ * class OrderController {
551
+ * group = "orders"; // Groups all actions under "orders" tag
552
+ *
553
+ * // GET /api/orders/search
554
+ * searchOrders = $action({
555
+ * name: "search",
556
+ * path: "/orders/search", // Custom path
557
+ * description: "Advanced order search with filtering",
558
+ * schema: {
559
+ * query: t.object({
560
+ * status: t.optional(t.union([
561
+ * t.literal("pending"),
562
+ * t.literal("processing"),
563
+ * t.literal("shipped"),
564
+ * t.literal("delivered"),
565
+ * t.literal("cancelled")
566
+ * ])),
567
+ * customerId: t.optional(t.string()),
568
+ * dateFrom: t.optional(t.date()),
569
+ * dateTo: t.optional(t.date()),
570
+ * minAmount: t.optional(t.number({ minimum: 0 })),
571
+ * maxAmount: t.optional(t.number({ minimum: 0 })),
572
+ * sortBy: t.optional(t.union([
573
+ * t.literal("createdAt"),
574
+ * t.literal("amount"),
575
+ * t.literal("status")
576
+ * ])),
577
+ * sortOrder: t.optional(t.enum(["asc", "desc"]))
578
+ * }),
579
+ * response: t.object({
580
+ * orders: t.array(t.object({
581
+ * id: t.string(),
582
+ * orderNumber: t.string(),
583
+ * customerId: t.string(),
584
+ * customerName: t.string(),
585
+ * status: t.string(),
586
+ * totalAmount: t.number(),
587
+ * createdAt: t.datetime(),
588
+ * itemCount: t.number()
589
+ * })),
590
+ * pagination: t.object({
591
+ * page: t.number(),
592
+ * limit: t.number(),
593
+ * total: t.number(),
594
+ * hasMore: t.boolean()
595
+ * }),
596
+ * filters: t.object({
597
+ * appliedFilters: t.array(t.string()),
598
+ * availableStatuses: t.array(t.string())
599
+ * })
600
+ * })
601
+ * },
602
+ * handler: async ({ query }) => {
603
+ * // Build dynamic query based on filters
604
+ * const searchCriteria = this.orderService.buildSearchCriteria(query);
605
+ * const results = await this.orderService.searchOrders(searchCriteria);
606
+ *
607
+ * return {
608
+ * orders: results.orders,
609
+ * pagination: results.pagination,
610
+ * filters: {
611
+ * appliedFilters: Object.keys(query).filter(key => query[key] !== undefined),
612
+ * availableStatuses: await this.orderService.getAvailableStatuses()
613
+ * }
614
+ * };
615
+ * }
616
+ * });
617
+ *
618
+ * // POST /api/orders/:id/process
619
+ * processOrder = $action({
620
+ * method: "POST",
621
+ * path: "/orders/:id/process",
622
+ * description: "Process an order through the fulfillment workflow",
623
+ * schema: {
624
+ * params: t.object({ id: t.string() }),
625
+ * body: t.object({
626
+ * notes: t.optional(t.string()),
627
+ * priority: t.optional(t.union([
628
+ * t.literal("low"),
629
+ * t.literal("normal"),
630
+ * t.literal("high"),
631
+ * t.literal("urgent")
632
+ * ])),
633
+ * assignToWarehouse: t.optional(t.string())
634
+ * }),
635
+ * response: t.object({
636
+ * orderId: t.string(),
637
+ * status: t.string(),
638
+ * processedAt: t.datetime(),
639
+ * estimatedFulfillment: t.datetime(),
640
+ * trackingInfo: t.optional(t.object({
641
+ * trackingNumber: t.string(),
642
+ * carrier: t.string(),
643
+ * estimatedDelivery: t.date()
644
+ * }))
645
+ * })
646
+ * },
647
+ * handler: async ({ params, body, user }) => {
648
+ * // Validate order can be processed
649
+ * const order = await this.orderService.findById(params.id);
650
+ * if (!order || order.status !== "pending") {
651
+ * throw new Error("Order cannot be processed in current status");
652
+ * }
653
+ *
654
+ * // Check inventory availability
655
+ * const inventoryCheck = await this.inventoryService.checkAvailability(order.items);
656
+ * if (!inventoryCheck.available) {
657
+ * throw new Error(`Insufficient inventory: ${inventoryCheck.missingItems.join(", ")}`);
658
+ * }
659
+ *
660
+ * // Process the order
661
+ * const processResult = await this.fulfillmentService.processOrder({
662
+ * orderId: params.id,
663
+ * options: {
664
+ * notes: body.notes,
665
+ * priority: body.priority || "normal",
666
+ * warehouse: body.assignToWarehouse
667
+ * }
668
+ * });
669
+ *
670
+ * // Update order status
671
+ * await this.orderService.updateStatus(params.id, "processing", {
672
+ * processedBy: user.id,
673
+ * processedAt: new Date(),
674
+ * notes: body.notes
675
+ * });
676
+ *
677
+ * // Send notification
678
+ * await this.notificationService.sendOrderUpdate(order.customerId, {
679
+ * orderId: params.id,
680
+ * status: "processing",
681
+ * message: "Your order is now being processed"
682
+ * });
683
+ *
684
+ * return {
685
+ * orderId: params.id,
686
+ * status: "processing",
687
+ * processedAt: new Date().toISOString(),
688
+ * estimatedFulfillment: processResult.estimatedCompletion,
689
+ * trackingInfo: processResult.trackingInfo
690
+ * };
691
+ * }
692
+ * });
256
693
  * }
257
- * // GET /api/hello -> "Hello World"
258
694
  * ```
695
+ *
696
+ * @example
697
+ * **Actions with security integration and role-based access:**
698
+ * ```ts
699
+ * class AdminController {
700
+ * group = "admin";
701
+ *
702
+ * // Only accessible to users with "admin:users:read" permission
703
+ * getUserStats = $action({
704
+ * description: "Get comprehensive user statistics",
705
+ * security: { permissions: ["admin:users:read"] },
706
+ * schema: {
707
+ * query: t.object({
708
+ * includeInactive: t.optional(t.boolean())
709
+ * }),
710
+ * response: t.object({
711
+ * totalUsers: t.number(),
712
+ * activeUsers: t.number(),
713
+ * newUsers: t.number(),
714
+ * userGrowth: t.number(),
715
+ * breakdown: t.object({
716
+ * byRole: t.record(t.string(), t.number()),
717
+ * byStatus: t.record(t.string(), t.number()),
718
+ * byRegistrationSource: t.record(t.string(), t.number())
719
+ * }),
720
+ * trends: t.array(t.object({
721
+ * date: t.date(),
722
+ * registrations: t.number(),
723
+ * activations: t.number()
724
+ * }))
725
+ * })
726
+ * },
727
+ * handler: async ({ query, user }) => {
728
+ * // user is available through security integration
729
+ * this.auditLogger.log({
730
+ * action: "admin.getUserStats",
731
+ * userId: user.id,
732
+ * userRole: user.role,
733
+ * timestamp: new Date()
734
+ * });
735
+ *
736
+ * const period = query.period || "month";
737
+ * const stats = await this.analyticsService.getUserStatistics({
738
+ * period,
739
+ * includeInactive: query.includeInactive || false
740
+ * });
741
+ *
742
+ * return stats;
743
+ * }
744
+ * });
745
+ *
746
+ * // Bulk operations with transaction support
747
+ * bulkUpdateUsers = $action({
748
+ * method: "POST",
749
+ * path: "/admin/users/bulk-update",
750
+ * description: "Bulk update user properties",
751
+ * security: { permissions: ["admin:users:write"] },
752
+ * schema: {
753
+ * body: t.object({
754
+ * userIds: t.array(t.string(), { minItems: 1, maxItems: 1000 }),
755
+ * updates: t.object({
756
+ * status: t.optional(t.union([t.literal("active"), t.literal("inactive")])),
757
+ * role: t.optional(t.string()),
758
+ * tags: t.optional(t.array(t.string())),
759
+ * customFields: t.optional(t.record(t.string(), t.any()))
760
+ * }),
761
+ * reason: t.string({ minLength: 10, maxLength: 500 })
762
+ * }),
763
+ * response: t.object({
764
+ * updated: t.number(),
765
+ * failed: t.number(),
766
+ * errors: t.array(t.object({
767
+ * userId: t.string(),
768
+ * error: t.string()
769
+ * })),
770
+ * auditLogId: t.string()
771
+ * })
772
+ * },
773
+ * handler: async ({ body, user }) => {
774
+ * const results = { updated: 0, failed: 0, errors: [] };
775
+ *
776
+ * // Create audit log entry
777
+ * const auditLogId = await this.auditService.logBulkOperation({
778
+ * operation: "bulk_user_update",
779
+ * initiatedBy: user.id,
780
+ * targetCount: body.userIds.length,
781
+ * reason: body.reason,
782
+ * changes: body.updates
783
+ * });
784
+ *
785
+ * // Process in batches to avoid overwhelming the database
786
+ * const batchSize = 50;
787
+ * for (let i = 0; i < body.userIds.length; i += batchSize) {
788
+ * const batch = body.userIds.slice(i, i + batchSize);
789
+ *
790
+ * try {
791
+ * const updateResult = await this.userService.bulkUpdate(batch, body.updates);
792
+ * results.updated += updateResult.success;
793
+ * results.failed += updateResult.failed;
794
+ * results.errors.push(...updateResult.errors);
795
+ * } catch (error) {
796
+ * // Log batch failure but continue processing
797
+ * this.logger.error(`Bulk update batch failed`, {
798
+ * batch: i / batchSize + 1,
799
+ * userIds: batch,
800
+ * error: error.message
801
+ * });
802
+ *
803
+ * results.failed += batch.length;
804
+ * results.errors.push(...batch.map(userId => ({
805
+ * userId,
806
+ * error: error.message
807
+ * })));
808
+ * }
809
+ * }
810
+ *
811
+ * // Update audit log with results
812
+ * await this.auditService.updateBulkOperationResults(auditLogId, results);
813
+ *
814
+ * return { ...results, auditLogId };
815
+ * }
816
+ * });
817
+ * }
818
+ * ```
819
+ *
820
+ * **Important Notes**:
821
+ * - Actions are automatically registered with the HTTP server when the service is initialized
822
+ * - Use `run()` for direct invocation (testing, internal calls, or remote services)
823
+ * - Use `fetch()` for explicit HTTP requests (client-side, external services)
824
+ * - Schema validation occurs automatically for all requests and responses
825
+ * - Path parameters are automatically extracted from schema definitions
826
+ * - Content-Type headers are automatically set based on schema types
827
+ * - Actions can be disabled via the `disabled` option for maintenance or feature flags
828
+ *
829
+ * @stability 2
259
830
  */
260
831
  declare const $action: {
261
832
  <TConfig extends RequestConfigSchema>(options: ActionDescriptorOptions<TConfig>): ActionDescriptor<TConfig>;
@@ -328,7 +899,7 @@ interface ActionDescriptorOptions<TConfig extends RequestConfigSchema> extends O
328
899
  handler: ServerActionHandler<TConfig>;
329
900
  }
330
901
  declare class ActionDescriptor<TConfig extends RequestConfigSchema> extends Descriptor<ActionDescriptorOptions<TConfig>> {
331
- protected readonly log: _alepha_logger0.Logger;
902
+ protected readonly log: _alepha_logger3.Logger;
332
903
  protected readonly env: {
333
904
  SERVER_API_PREFIX: string;
334
905
  };
@@ -362,7 +933,7 @@ declare class ActionDescriptor<TConfig extends RequestConfigSchema> extends Desc
362
933
  * Call the action handler directly.
363
934
  * There is no HTTP layer involved.
364
935
  */
365
- run(config: ClientRequestEntry<TConfig>, options?: ClientRequestOptions): Promise<ClientRequestResponse<TConfig>>;
936
+ run(config?: ClientRequestEntry<TConfig>, options?: ClientRequestOptions): Promise<ClientRequestResponse<TConfig>>;
366
937
  /**
367
938
  * Works like `run`, but always fetches (http request) the route.
368
939
  */
@@ -395,40 +966,35 @@ type ServerActionHandler<TConfig extends RequestConfigSchema = RequestConfigSche
395
966
  */
396
967
  interface ServerActionRequest<TConfig extends RequestConfigSchema> extends ServerRequest<TConfig> {}
397
968
  //#endregion
969
+ //#region src/schemas/errorSchema.d.ts
970
+ declare const errorSchema: typebox0.TObject<{
971
+ error: typebox0.TString;
972
+ status: typebox0.TInteger;
973
+ message: typebox0.TString;
974
+ details: typebox0.TOptional<typebox0.TString>;
975
+ requestId: typebox0.TOptional<typebox0.TString>;
976
+ cause: typebox0.TOptional<typebox0.TObject<{
977
+ name: typebox0.TString;
978
+ message: typebox0.TString;
979
+ }>>;
980
+ }>;
981
+ type ErrorSchema = Static<typeof errorSchema>;
982
+ //#endregion
398
983
  //#region src/errors/HttpError.d.ts
399
984
  declare const isHttpError: (error: unknown, status?: number) => error is HttpErrorLike;
400
985
  declare class HttpError extends AlephaError {
401
986
  name: string;
402
987
  static is: (error: unknown, status?: number) => error is HttpErrorLike;
403
- static toJSON(error: HttpError): {
404
- status: number;
405
- error: string | undefined;
406
- message: string;
407
- cause: {
408
- name: string;
409
- message: string;
410
- };
411
- } | {
412
- status: number;
413
- error: string | undefined;
414
- message: string;
415
- cause?: undefined;
416
- };
988
+ static toJSON(error: HttpError): ErrorSchema;
989
+ readonly error: string;
417
990
  readonly status: number;
418
- readonly error?: string;
991
+ readonly requestId?: string;
992
+ readonly details?: string;
419
993
  readonly reason?: {
420
994
  name: string;
421
995
  message: string;
422
996
  };
423
- constructor(options: {
424
- error?: string;
425
- message: string;
426
- status: number;
427
- cause?: {
428
- name: string;
429
- message: string;
430
- } | unknown;
431
- }, cause?: unknown);
997
+ constructor(options: Partial<ErrorSchema>, cause?: unknown);
432
998
  }
433
999
  declare const errorNameByStatus: Record<number, string>;
434
1000
  interface HttpErrorLike extends Error {
@@ -485,35 +1051,26 @@ declare class ValidationError extends HttpError {
485
1051
  }
486
1052
  //#endregion
487
1053
  //#region src/helpers/isMultipart.d.ts
1054
+ /**
1055
+ * Checks if the route has multipart/form-data request body.
1056
+ */
488
1057
  declare const isMultipart: (options: {
489
1058
  schema?: RequestConfigSchema;
490
1059
  requestBodyType?: string;
491
1060
  }) => boolean;
492
1061
  //#endregion
493
- //#region src/schemas/errorSchema.d.ts
494
- declare const errorSchema: _sinclair_typebox7.TObject<{
495
- error: _sinclair_typebox7.TString;
496
- status: _sinclair_typebox7.TNumber;
497
- message: _sinclair_typebox7.TString;
498
- details: _sinclair_typebox7.TOptional<_sinclair_typebox7.TString>;
499
- cause: _sinclair_typebox7.TOptional<_sinclair_typebox7.TObject<{
500
- name: _sinclair_typebox7.TString;
501
- message: _sinclair_typebox7.TString;
502
- }>>;
503
- }>;
504
- //#endregion
505
1062
  //#region src/schemas/okSchema.d.ts
506
- declare const okSchema: _sinclair_typebox7.TObject<{
507
- ok: _sinclair_typebox7.TBoolean;
508
- id: _sinclair_typebox7.TOptional<_sinclair_typebox7.TUnion<[_sinclair_typebox7.TString, _sinclair_typebox7.TInteger]>>;
509
- count: _sinclair_typebox7.TOptional<_sinclair_typebox7.TNumber>;
1063
+ declare const okSchema: typebox0.TObject<{
1064
+ ok: typebox0.TBoolean;
1065
+ id: typebox0.TOptional<typebox0.TUnion<[typebox0.TString, typebox0.TInteger]>>;
1066
+ count: typebox0.TOptional<typebox0.TNumber>;
510
1067
  }>;
511
1068
  type Ok = Static<typeof okSchema>;
512
1069
  //#endregion
513
1070
  //#region src/providers/NodeHttpServerProvider.d.ts
514
- declare const envSchema: _alepha_core1.TObject<{
515
- SERVER_PORT: _alepha_core1.TNumber;
516
- SERVER_HOST: _alepha_core1.TString;
1071
+ declare const envSchema: _alepha_core11.TObject<{
1072
+ SERVER_PORT: _alepha_core11.TInteger;
1073
+ SERVER_HOST: _alepha_core11.TString;
517
1074
  }>;
518
1075
  declare module "alepha" {
519
1076
  interface Env extends Partial<Static<typeof envSchema>> {}
@@ -521,31 +1078,31 @@ declare module "alepha" {
521
1078
  declare class NodeHttpServerProvider extends ServerProvider {
522
1079
  protected readonly alepha: Alepha;
523
1080
  protected readonly dateTimeProvider: DateTimeProvider;
524
- protected readonly log: _alepha_logger0.Logger;
1081
+ protected readonly log: _alepha_logger3.Logger;
525
1082
  protected readonly env: {
526
1083
  SERVER_PORT: number;
527
1084
  SERVER_HOST: string;
528
1085
  };
529
1086
  protected readonly router: ServerRouterProvider;
530
1087
  protected readonly server: http0.Server<typeof IncomingMessage, typeof ServerResponse$1>;
531
- protected readonly onNodeRequest: _alepha_core1.HookDescriptor<"node:request">;
1088
+ protected readonly onNodeRequest: _alepha_core11.HookDescriptor<"node:request">;
532
1089
  handle(req: IncomingMessage, res: ServerResponse$1): Promise<void>;
533
1090
  createRouterRequest(req: IncomingMessage, res: ServerResponse$1, params?: Record<string, string>): ServerRawRequest;
534
1091
  getProtocol(req: IncomingMessage): "http" | "https";
535
1092
  get hostname(): string;
536
- readonly start: _alepha_core1.HookDescriptor<"start">;
537
- protected readonly stop: _alepha_core1.HookDescriptor<"stop">;
1093
+ readonly start: _alepha_core11.HookDescriptor<"start">;
1094
+ protected readonly stop: _alepha_core11.HookDescriptor<"stop">;
538
1095
  protected listen(): Promise<void>;
539
1096
  protected close(): Promise<void>;
540
1097
  }
541
1098
  //#endregion
542
1099
  //#region src/providers/ServerLoggerProvider.d.ts
543
1100
  declare class ServerLoggerProvider {
544
- protected readonly log: _alepha_logger0.Logger;
1101
+ protected readonly log: _alepha_logger3.Logger;
545
1102
  protected readonly alepha: Alepha;
546
- readonly onRequest: _alepha_core1.HookDescriptor<"server:onRequest">;
547
- readonly onError: _alepha_core1.HookDescriptor<"server:onError">;
548
- readonly onResponse: _alepha_core1.HookDescriptor<"server:onResponse">;
1103
+ readonly onRequest: _alepha_core11.HookDescriptor<"server:onRequest">;
1104
+ readonly onError: _alepha_core11.HookDescriptor<"server:onError">;
1105
+ readonly onResponse: _alepha_core11.HookDescriptor<"server:onResponse">;
549
1106
  }
550
1107
  //#endregion
551
1108
  //#region src/providers/ServerNotReadyProvider.d.ts
@@ -558,7 +1115,7 @@ declare class ServerLoggerProvider {
558
1115
  */
559
1116
  declare class ServerNotReadyProvider {
560
1117
  protected readonly alepha: Alepha;
561
- readonly onRequest: _alepha_core1.HookDescriptor<"server:onRequest">;
1118
+ readonly onRequest: _alepha_core11.HookDescriptor<"server:onRequest">;
562
1119
  }
563
1120
  //#endregion
564
1121
  //#region src/index.d.ts
@@ -626,7 +1183,7 @@ declare module "alepha" {
626
1183
  * @see {@link $action}
627
1184
  * @module alepha.server
628
1185
  */
629
- declare const AlephaServer: _alepha_core1.Service<_alepha_core1.Module>;
1186
+ declare const AlephaServer: _alepha_core11.Service<_alepha_core11.Module>;
630
1187
  //#endregion
631
- export { $action, $route, ActionDescriptor, ActionDescriptorOptions, AlephaServer, BadRequestError, ClientRequestEntry, ClientRequestEntryContainer, ClientRequestOptions, ClientRequestResponse, ConflictError, FetchActionArgs, FetchOptions, FetchResponse, ForbiddenError, HttpAction, HttpClient, HttpClientPendingRequests, HttpError, HttpErrorLike, NodeHttpServerProvider, NotFoundError, Ok, RequestConfigSchema, ResponseBodyType, ResponseKind, RouteDescriptor, RouteDescriptorOptions, RouteMethod, ServerActionHandler, ServerActionRequest, ServerHandler, ServerLoggerProvider, ServerMiddlewareHandler, ServerNotReadyProvider, ServerProvider, ServerRawRequest, ServerReply, ServerRequest, ServerRequestConfig, ServerRequestConfigEntry, ServerResponse, ServerResponseBody, ServerRoute, ServerRouteMatcher, ServerRouterProvider, ServerTimingProvider, UnauthorizedError, ValidationError, errorNameByStatus, errorSchema, isHttpError, isMultipart, okSchema, routeMethods };
1188
+ export { $action, $route, ActionDescriptor, ActionDescriptorOptions, AlephaServer, BadRequestError, ClientRequestEntry, ClientRequestEntryContainer, ClientRequestOptions, ClientRequestResponse, ConflictError, ErrorSchema, FetchActionArgs, FetchOptions, FetchResponse, ForbiddenError, HttpAction, HttpClient, HttpClientPendingRequests, HttpError, HttpErrorLike, NodeHttpServerProvider, NotFoundError, Ok, RequestConfigSchema, ResponseBodyType, ResponseKind, RouteDescriptor, RouteDescriptorOptions, RouteMethod, ServerActionHandler, ServerActionRequest, ServerHandler, ServerLoggerProvider, ServerNotReadyProvider, ServerProvider, ServerRawRequest, ServerReply, ServerRequest, ServerRequestConfig, ServerRequestConfigEntry, ServerResponse, ServerResponseBody, ServerRoute, ServerRouteMatcher, ServerRouteRequestHandler, ServerRouterProvider, ServerTimingProvider, TRequestBody, TResponseBody, UnauthorizedError, ValidationError, errorNameByStatus, errorSchema, isHttpError, isMultipart, okSchema, routeMethods };
632
1189
  //# sourceMappingURL=index.d.ts.map