alepha 0.9.5 → 0.10.1

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/README.md CHANGED
@@ -10,7 +10,6 @@
10
10
  Alepha
11
11
  </h1>
12
12
  <p style="max-width: 512px">
13
- 🚧
14
13
  </p>
15
14
  <a href="https://www.npmjs.com/package/alepha"><img src="https://img.shields.io/npm/v/alepha.svg" alt="npm"/></a>
16
15
  <a href="https://www.npmjs.com/package/alepha"><img src="https://img.shields.io/npm/l/alepha.svg" alt="npm"/></a>
@@ -19,17 +18,23 @@ Alepha
19
18
  <a href="https://github.com/feunard/alepha"><img src="https://img.shields.io/github/stars/feunard/alepha.svg?style=social" alt="GitHub stars"/></a>
20
19
  </div>
21
20
 
22
- Alepha is a convention-driven TypeScript framework for building robust, end-to-end type-safe applications, from serverless APIs to full-stack React apps.
21
+ A convention-driven TypeScript framework for building type-safe full-stack applications.
23
22
 
24
- ## Installation
23
+ ## Quick Start
24
+
25
+ ```bash
26
+ npx @alepha/cli create my-app
27
+ ```
28
+
29
+ Or manually:
25
30
 
26
31
  ```bash
27
32
  npm install alepha
28
33
  ```
29
34
 
30
- ## Usage
35
+ ## What is this?
31
36
 
32
- Minimalist http server with a single endpoint.
37
+ Alepha is an opinionated framework that handles everything from database to frontend. It uses a descriptor-based architecture (`$action`, `$page`, `$repository`, etc.) and enforces type safety across the entire stack.
33
38
 
34
39
  ```ts
35
40
  import { run } from "alepha";
@@ -44,50 +49,84 @@ class App {
44
49
  run(App);
45
50
  ```
46
51
 
47
- 👉 For more information, please visit the [documentation](https://feunard.github.io/alepha/).
52
+ ## Examples
48
53
 
49
- ## Modules
54
+ ### Type-safe API endpoint
50
55
 
51
- Alepha is modular, with a LOT of modules.
56
+ ```ts
57
+ import { $action } from "alepha/server";
58
+ import { t } from "alepha/core";
59
+
60
+ class UserController {
61
+ getUser = $action({
62
+ schema: {
63
+ params: t.object({ id: t.string() }),
64
+ response: t.object({
65
+ name: t.string(),
66
+ email: t.string()
67
+ })
68
+ },
69
+ handler: async ({ params }) => {
70
+ return { name: "John", email: "john@example.com" };
71
+ }
72
+ });
73
+ }
74
+ ```
52
75
 
53
- ### Core & Application Layer
76
+ ### Database with Drizzle ORM
54
77
 
55
- * **Core ([@alepha/core](https://feunard.github.io/alepha/docs/alepha-core)) 📦:** The heart of the framework, providing a powerful dependency injection container, application lifecycle management, and the core descriptor system.
56
- * **Server ([@alepha/server](https://feunard.github.io/alepha/docs/alepha-server)) 🌐:** A high-performance, minimalist HTTP server for creating type-safe REST APIs using declarative `$action` descriptors.
57
- * **Database ([@alepha/postgres](https://feunard.github.io/alepha/docs/alepha-postgres)) 🗄️:** A powerful and type-safe ORM built on Drizzle. Define your schema with `$entity` and get fully-typed repositories with `$repository`.
58
- * **React ([@alepha/react](https://feunard.github.io/alepha/docs/alepha-react)) ⚛️:** Build full-stack, server-side rendered React applications with a file-based routing system (`$page`) that handles data fetching, hydration, and type-safe props.
78
+ ```ts
79
+ import {$entity, $repository, pg} from "alepha/postgres";
80
+ import {t, Static} from "alepha";
59
81
 
60
- ### Backend Infrastructure & Abstractions
82
+ export const users = $entity({
83
+ id: pg.primaryKey(),
84
+ name: t.string(),
85
+ email: t.string()
86
+ });
61
87
 
62
- * **Security ([@alepha/security](https://feunard.github.io/alepha/docs/alepha-security)) 🛡️:** A complete authentication and authorization system. Manage roles (`$role`), permissions (`$permission`), JWTs, and realms (`$realm`).
63
- * **Queue ([@alepha/queue](https://feunard.github.io/alepha/docs/alepha-queue)) ⏳:** A simple and robust interface for background job processing. Define workers with the `$queue` descriptor and integrate with backends like Redis.
64
- * **Cache ([@alepha/cache](https://feunard.github.io/alepha/docs/alepha-cache)) ⚡:** A flexible caching layer with support for TTL, automatic function caching (`$cache`), and multiple backends like in-memory or Redis.
65
- * **Bucket ([@alepha/bucket](https://feunard.github.io/alepha/docs/alepha-bucket)) ☁️:** A unified API for file and object storage. Abstract away the details of local, in-memory, or cloud storage providers like Azure Blob Storage.
66
- * **Scheduler ([@alepha/scheduler](https://feunard.github.io/alepha/docs/alepha-scheduler)) ⏰:** Schedule recurring tasks using cron expressions or fixed intervals with the `$scheduler` descriptor, with built-in support for distributed locking.
67
- * **Topic ([@alepha/topic](https://feunard.github.io/alepha/docs/alepha-topic)) 📢:** A publish-subscribe (pub/sub) messaging interface for building event-driven architectures with `$topic` and `$subscriber`.
68
- * **Lock ([@alepha/lock](https://feunard.github.io/alepha/docs/alepha-lock)) 🔒:** A distributed locking mechanism to ensure safe concurrent access to shared resources, using Redis or other backends.
88
+ type CreateUser = Static<typeof users.$insertSchema>;
69
89
 
70
- ### Server Middleware & Plugins
90
+ class UserService {
91
+ users = $repository(users);
71
92
 
72
- * **Links ([@alepha/server-links](https://feunard.github.io/alepha/docs/alepha-server-links)) 🔗:** Enables end-to-end type-safe communication between your frontend and backend, or between microservices, with the `$client` descriptor.
73
- * **Swagger ([@alepha/server-swagger](https://feunard.github.io/alepha/docs/alepha-server-swagger)) 📜:** Automatically generate OpenAPI 3.0 documentation and a beautiful Swagger UI for all your `$action` API endpoints.
74
- * **Helmet ([@alepha/server-helmet](https://feunard.github.io/alepha/docs/alepha-server-helmet)) 🎩:** Enhance your application's security by automatically applying essential HTTP security headers like CSP and HSTS.
75
- * **CORS ([@alepha/server-cors](https://feunard.github.io/alepha/docs/alepha-server-cors)) ↔️:** A configurable middleware to handle Cross-Origin Resource Sharing (CORS) for your server.
76
- * **Multipart ([@alepha/server-multipart](https://feunard.github.io/alepha/docs/alepha-server-multipart)) 📎:** Seamlessly handle `multipart/form-data` requests for file uploads.
77
- * **Compress ([@alepha/server-compress](https://feunard.github.io/alepha/docs/alepha-server-compress)) 📦💨:** Automatically compress server responses with Gzip or Brotli to improve performance.
93
+ async create(data: CreateUser) {
94
+ return await this.users.create(data);
95
+ }
96
+ }
97
+ ```
98
+
99
+ ### React SSR Page
78
100
 
79
- And more, like **Request Logging**, **Error Handling**, and **Response Caching**, cookie parsers, and more, to enhance your server's capabilities.
101
+ ```tsx
102
+ import { $page } from "alepha/react";
80
103
 
81
- ### Full-Stack & React Ecosystem
104
+ class HomePage {
105
+ index = $page({
106
+ component: () => <div>Hello from React SSR!</div>
107
+ });
108
+ }
109
+ ```
82
110
 
83
- * **Auth ([@alepha/react-auth](https://feunard.github.io/alepha/docs/alepha-react-auth)) 🔑:** Simplifies frontend authentication flows, providing the `useAuth` hook to manage user sessions and permissions in your React components.
84
- * **Head ([@alepha/react-head](https://feunard.github.io/alepha/docs/alepha-react-head)) SEO:** Manage your document's `<head>` for SEO and metadata. Control titles, meta tags, and more, both on the server and client.
85
- * **i18n ([@alepha/react-i18n](https://feunard.github.io/alepha/docs/alepha-react-i18n)) 🌍:** A complete internationalization solution for your React applications, with support for lazy-loaded translation files and the `useI18n` hook.
86
- * **Form ([@alepha/react-form](https://feunard.github.io/alepha/docs/alepha-react-form)) 📝:** Create powerful, type-safe forms with automatic validation using the `useForm` hook, powered by your TypeBox schemas.
111
+ ## Core Concepts
87
112
 
88
- ### Tooling & Utilities
113
+ - **Descriptors**: Define your app logic with `$action`, `$page`, `$repository`, `$cache`, `$email`, etc.
114
+ - **Type Safety**: TypeBox schemas validate data from DB to API to frontend
115
+ - **DI Container**: Built-in dependency injection using `$inject()`
116
+ - **Convention over Config**: Minimal boilerplate, sensible defaults
117
+ - **Full-Stack**: React SSR, Vite, class-based router with type-safe routing
118
+
119
+ ## Stack
120
+
121
+ - Node.js 22+
122
+ - TypeScript
123
+ - React (SSR)
124
+ - Vite
125
+ - Drizzle ORM
126
+ - PostgreSQL
127
+
128
+ 👉 For more information, please visit the [documentation](https://feunard.github.io/alepha/).
89
129
 
90
- * **Vite ([@alepha/vite](https://feunard.github.io/alepha/docs/alepha-vite)) ✨:** A seamless Vite plugin that handles all the complex build and development server configurations for your full-stack Alepha applications.
91
- * **Command ([@alepha/command](https://feunard.github.io/alepha/docs/alepha-command)) ⌨️:** Build powerful, type-safe command-line interfaces and scripts directly within your application using the `$command` descriptor.
92
- * **Retry ([@alepha/retry](https://feunard.github.io/alepha/docs/alepha-retry)) 🔄:** A declarative and powerful decorator (`$retry`) for automatically retrying failed operations with exponential backoff.
130
+ ## License
93
131
 
132
+ MIT
package/batch.d.ts CHANGED
@@ -4,6 +4,7 @@ import { DateTimeProvider, DurationLike } from "alepha/datetime";
4
4
  import * as _alepha_logger0 from "alepha/logger";
5
5
  import * as _alepha_retry0 from "alepha/retry";
6
6
  import { RetryDescriptorOptions } from "alepha/retry";
7
+ import * as typebox0 from "typebox";
7
8
 
8
9
  //#region src/descriptors/$batch.d.ts
9
10
 
@@ -532,9 +533,7 @@ declare class BatchDescriptor<TItem extends TSchema, TResponse = any> extends De
532
533
  protected readonly dateTime: DateTimeProvider;
533
534
  protected readonly partitions: Map<any, any>;
534
535
  protected activeHandlers: PromiseWithResolvers<void>[];
535
- protected retry: _alepha_retry0.RetryDescriptorFn<(items: (TItem & {
536
- params: [];
537
- })["static"][]) => TResponse>;
536
+ protected retry: _alepha_retry0.RetryDescriptorFn<(items: typebox0.StaticType<[], "Encode", {}, {}, TItem>[]) => TResponse>;
538
537
  /**
539
538
  * Pushes an item into the batch. The item will be processed
540
539
  * asynchronously with other items when the batch is flushed.
package/bucket.d.ts CHANGED
@@ -2,7 +2,7 @@ import * as _alepha_core1 from "alepha";
2
2
  import { Alepha, AlephaError, Descriptor, FileLike, KIND, Service } from "alepha";
3
3
  import * as fs from "node:fs";
4
4
  import * as _alepha_logger0 from "alepha/logger";
5
- import * as _sinclair_typebox0 from "@sinclair/typebox";
5
+ import * as typebox0 from "typebox";
6
6
 
7
7
  //#region src/providers/FileStorageProvider.d.ts
8
8
  declare abstract class FileStorageProvider {
@@ -519,7 +519,7 @@ interface BucketFileOptions {
519
519
  maxSize?: number;
520
520
  }
521
521
  declare class BucketDescriptor extends Descriptor<BucketDescriptorOptions> {
522
- readonly provider: MemoryFileStorageProvider | FileStorageProvider;
522
+ readonly provider: FileStorageProvider | MemoryFileStorageProvider;
523
523
  get name(): string;
524
524
  /**
525
525
  * Uploads a file to the bucket.
@@ -537,7 +537,7 @@ declare class BucketDescriptor extends Descriptor<BucketDescriptorOptions> {
537
537
  * Downloads a file from the bucket.
538
538
  */
539
539
  download(fileId: string): Promise<FileLike>;
540
- protected $provider(): MemoryFileStorageProvider | FileStorageProvider;
540
+ protected $provider(): FileStorageProvider | MemoryFileStorageProvider;
541
541
  }
542
542
  interface BucketFileOptions {
543
543
  /**
@@ -579,10 +579,10 @@ declare class LocalFileStorageProvider implements FileStorageProvider {
579
579
  protected path(container: string, fileId?: string): string;
580
580
  protected isErrorNoEntry(error: unknown): boolean;
581
581
  }
582
- declare const fileMetadataSchema: _sinclair_typebox0.TObject<{
583
- name: _sinclair_typebox0.TString;
584
- type: _sinclair_typebox0.TString;
585
- size: _sinclair_typebox0.TNumber;
582
+ declare const fileMetadataSchema: typebox0.TObject<{
583
+ name: typebox0.TString;
584
+ type: typebox0.TString;
585
+ size: typebox0.TNumber;
586
586
  }>;
587
587
  //#endregion
588
588
  //#region src/index.d.ts
package/command.d.ts CHANGED
@@ -1,10 +1,51 @@
1
1
  import * as _alepha_core1 from "alepha";
2
- import { Alepha, AlephaError, Async, Descriptor, KIND, Static, TObject, TSchema } from "alepha";
2
+ import { Alepha, AlephaError, Async, Descriptor, KIND, Static, TObject, TSchema, TString } from "alepha";
3
+ import * as _alepha_logger0 from "alepha/logger";
3
4
  import * as fs from "node:fs/promises";
4
5
  import { glob } from "node:fs/promises";
5
- import * as _alepha_logger0 from "alepha/logger";
6
- import * as _sinclair_typebox0 from "@sinclair/typebox";
6
+ import * as readline_promises0 from "readline/promises";
7
+ import * as typebox0 from "typebox";
7
8
 
9
+ //#region src/helpers/Asker.d.ts
10
+ interface AskOptions<T extends TSchema = TString> {
11
+ /**
12
+ * Response schema expected.
13
+ *
14
+ * Recommended schemas:
15
+ * - t.string() - for free text input
16
+ * - t.number() - for numeric input
17
+ * - t.boolean() - for yes/no input (accepts "true", "false", "1", "0")
18
+ * - t.enum(["option1", "option2"]) - for predefined options
19
+ *
20
+ * You can use schema.default to provide a default value.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * ask("What is your name?", { schema: t.string({ default: "John Doe" }) })
25
+ * ```
26
+ *
27
+ * @default TString
28
+ */
29
+ schema?: T;
30
+ /**
31
+ * Custom validation function.
32
+ * Throws an AlephaError in case of validation failure.
33
+ */
34
+ validate?: (value: Static<T>) => void;
35
+ }
36
+ interface AskMethod {
37
+ <T extends TSchema = TString>(question: string, options?: AskOptions<T>): Promise<Static<T>>;
38
+ }
39
+ declare class Asker {
40
+ protected readonly log: _alepha_logger0.Logger;
41
+ readonly ask: AskMethod;
42
+ protected readonly alepha: Alepha;
43
+ constructor();
44
+ protected createAskMethod(): AskMethod;
45
+ protected prompt<T extends TSchema = TString>(question: string, options: AskOptions<T>): Promise<Static<T>>;
46
+ protected createPromptInterface(): readline_promises0.Interface;
47
+ }
48
+ //#endregion
8
49
  //#region src/helpers/Runner.d.ts
9
50
  type Task = {
10
51
  name: string;
@@ -19,13 +60,9 @@ interface RunOptions {
19
60
  * Rename the command for logging purposes.
20
61
  */
21
62
  alias?: string;
22
- /**
23
- * If true, the command will not be logged.
24
- */
25
- silent?: boolean;
26
63
  }
27
64
  interface RunnerMethod {
28
- (cmd: string | Array<string | Task>, fn?: () => any, options?: RunOptions): Promise<string>;
65
+ (cmd: string | Task | Array<string | Task>, options?: RunOptions | (() => any)): Promise<string>;
29
66
  rm: (glob: string | string[], options?: RunOptions) => Promise<string>;
30
67
  cp: (source: string, dest: string, options?: RunOptions) => Promise<string>;
31
68
  }
@@ -59,14 +96,14 @@ declare class Runner {
59
96
  * within your Alepha application structure.
60
97
  */
61
98
  declare const $command: {
62
- <T extends TObject>(options: CommandDescriptorOptions<T>): CommandDescriptor<T>;
99
+ <T extends TObject, A extends TSchema>(options: CommandDescriptorOptions<T, A>): CommandDescriptor<T, A>;
63
100
  [KIND]: typeof CommandDescriptor;
64
101
  };
65
- interface CommandDescriptorOptions<T extends TObject> {
102
+ interface CommandDescriptorOptions<T extends TObject, A extends TSchema> {
66
103
  /**
67
104
  * The handler function to execute when the command is matched.
68
105
  */
69
- handler: (args: CommandHandlerArgs<T>) => Async<void>;
106
+ handler: (args: CommandHandlerArgs<T, A>) => Async<void>;
70
107
  /**
71
108
  * The name of the command. If omitted, the property key is used.
72
109
  *
@@ -85,19 +122,38 @@ interface CommandDescriptorOptions<T extends TObject> {
85
122
  * A TypeBox object schema defining the flags for the command.
86
123
  */
87
124
  flags?: T;
125
+ /**
126
+ * An optional TypeBox schema defining the arguments for the command.
127
+ *
128
+ * @example
129
+ * args: t.string()
130
+ * my-cli command <arg1: string>
131
+ *
132
+ * args: t.optional(t.string())
133
+ * my-cli command [arg1: string]
134
+ *
135
+ * args: t.tuple([t.string(), t.number()])
136
+ * my-cli command <arg1: string> <arg2: number>
137
+ *
138
+ * args: t.tuple([t.string(), t.optional(t.number())])
139
+ * my-cli command <arg1: string> [arg2: number]
140
+ */
141
+ args?: A;
88
142
  /**
89
143
  * If false, skip summary message at the end of the command execution.
90
144
  */
91
145
  summary?: boolean;
92
146
  }
93
- declare class CommandDescriptor<T extends TObject = TObject> extends Descriptor<CommandDescriptorOptions<T>> {
147
+ declare class CommandDescriptor<T extends TObject = TObject, A extends TSchema = TSchema> extends Descriptor<CommandDescriptorOptions<T, A>> {
94
148
  readonly flags: TObject<{}>;
95
149
  readonly aliases: string[];
96
150
  get name(): string;
97
151
  }
98
- interface CommandHandlerArgs<T extends TObject> {
152
+ interface CommandHandlerArgs<T extends TObject, A extends TSchema = TSchema> {
99
153
  flags: Static<T>;
154
+ args: A extends TSchema ? Static<A> : undefined;
100
155
  run: RunnerMethod;
156
+ ask: AskMethod;
101
157
  glob: typeof glob;
102
158
  fs: typeof fs;
103
159
  }
@@ -123,6 +179,7 @@ declare class CliProvider {
123
179
  protected readonly alepha: Alepha;
124
180
  protected readonly log: _alepha_logger0.Logger;
125
181
  protected readonly runner: Runner;
182
+ protected readonly asker: Asker;
126
183
  options: {
127
184
  name: string;
128
185
  description: string;
@@ -132,7 +189,7 @@ declare class CliProvider {
132
189
  help: {
133
190
  aliases: string[];
134
191
  description: string;
135
- schema: _sinclair_typebox0.TBoolean;
192
+ schema: typebox0.TBoolean;
136
193
  };
137
194
  };
138
195
  protected readonly onReady: _alepha_core1.HookDescriptor<"ready">;
@@ -144,6 +201,10 @@ declare class CliProvider {
144
201
  aliases: string[];
145
202
  schema: TSchema;
146
203
  }[]): Record<string, any>;
204
+ protected parseCommandArgs(argv: string[], schema?: TSchema): any;
205
+ protected parseArgumentValue(value: string, schema: TSchema): any;
206
+ protected generateArgsUsage(schema?: TSchema): string;
207
+ protected getTypeName(schema: TSchema): string;
147
208
  printHelp(command?: CommandDescriptor<any>): void;
148
209
  private getMaxCmdLength;
149
210
  private getMaxFlagLength;
@@ -160,7 +221,7 @@ declare class CliProvider {
160
221
  * @module alepha.command
161
222
  */
162
223
  declare const AlephaCommand: _alepha_core1.Service<_alepha_core1.Module>;
163
- declare module "@sinclair/typebox" {
224
+ declare module "typebox" {
164
225
  interface StringOptions {
165
226
  /**
166
227
  * Additional aliases for the flags.
@@ -173,5 +234,5 @@ declare module "@sinclair/typebox" {
173
234
  //# sourceMappingURL=index.d.ts.map
174
235
 
175
236
  //#endregion
176
- export { $command, AlephaCommand, CliProvider, CommandDescriptor, CommandDescriptorOptions, CommandError, CommandHandlerArgs, RunOptions, Runner, RunnerMethod, Task };
237
+ export { $command, AlephaCommand, AskMethod, AskOptions, Asker, CliProvider, CommandDescriptor, CommandDescriptorOptions, CommandError, CommandHandlerArgs, RunOptions, Runner, RunnerMethod, Task };
177
238
  //# sourceMappingURL=index.d.ts.map