alepha 0.9.3 → 0.9.5

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