@alepha/ui 0.10.4

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Feunard
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,329 @@
1
+ # Alepha Batch
2
+
3
+ Efficiently process operations in groups by size or time.
4
+
5
+ ## Installation
6
+
7
+ This package is part of the Alepha framework and can be installed via the all-in-one package:
8
+
9
+ ```bash
10
+ npm install alepha
11
+ ```
12
+
13
+ ## Module
14
+
15
+ This module allows you to group multiple asynchronous operations into a single "batch," which is then processed together.
16
+ This is an essential pattern for improving performance, reducing I/O, and interacting efficiently with rate-limited APIs or databases.
17
+
18
+ ```ts
19
+ import { Alepha, $hook, run, t } from "alepha";
20
+ import { $batch } from "alepha/batch";
21
+
22
+ class LoggingService {
23
+ // define the batch processor
24
+ logBatch = $batch({
25
+ schema: t.text(),
26
+ maxSize: 10,
27
+ maxDuration: [5, "seconds"],
28
+ handler: async (items) => {
29
+ console.log(`[BATCH LOG] Processing ${items.length} events:`, items);
30
+ },
31
+ });
32
+
33
+ // example of how to use it
34
+ onReady = $hook({
35
+ on: "ready",
36
+ handler: async () => {
37
+ this.logBatch.push("Application started.");
38
+ this.logBatch.push("User authenticated.");
39
+ // ... more events pushed from elsewhere in the app
40
+ },
41
+ });
42
+ }
43
+ ```
44
+
45
+ This module can be imported and used as follows:
46
+
47
+ ```typescript
48
+ import { Alepha, run } from "alepha";
49
+ import { AlephaBatch } from "alepha/batch";
50
+
51
+ const alepha = Alepha.create()
52
+ .with(AlephaBatch);
53
+
54
+ run(alepha);
55
+ ```
56
+
57
+ ## API Reference
58
+
59
+ ### Descriptors
60
+
61
+ Descriptors are functions that define and configure various aspects of your application. They follow the convention of starting with ` $ ` and return configured descriptor instances.
62
+
63
+ For more details, see the [Descriptors documentation](/docs/descriptors).
64
+
65
+ #### $batch()
66
+
67
+ Creates a batch processing descriptor for efficient grouping and processing of multiple operations.
68
+
69
+ This descriptor provides a powerful batching mechanism that collects multiple individual items
70
+ and processes them together in groups, significantly improving performance by reducing overhead
71
+ and enabling bulk operations. It supports partitioning, concurrent processing, automatic flushing,
72
+ and intelligent retry mechanisms for robust batch processing workflows.
73
+
74
+ **Key Features**
75
+
76
+ - **Intelligent Batching**: Groups items based on size and time thresholds
77
+ - **Partitioning Support**: Process different types of items in separate batches
78
+ - **Concurrent Processing**: Handle multiple batches simultaneously with configurable limits
79
+ - **Automatic Flushing**: Time-based and size-based automatic batch execution
80
+ - **Type Safety**: Full TypeScript support with schema validation using TypeBox
81
+ - **Retry Logic**: Built-in retry mechanisms for failed batch operations
82
+ - **Resource Management**: Automatic cleanup and graceful shutdown handling
83
+
84
+ **Use Cases**
85
+
86
+ Perfect for optimizing high-throughput operations:
87
+ - Database bulk inserts and updates
88
+ - API call batching and rate limit optimization
89
+ - Log aggregation and bulk shipping
90
+ - File processing and bulk uploads
91
+ - Event processing and analytics ingestion
92
+ - Notification delivery optimization
93
+ - Cache invalidation batching
94
+
95
+ **Basic database batch operations:**
96
+ ```ts
97
+ import { $batch } from "alepha/batch";
98
+ import { t } from "alepha";
99
+
100
+ class UserService {
101
+ userBatch = $batch({
102
+ schema: t.object({
103
+ id: t.text(),
104
+ name: t.text(),
105
+ email: t.text(),
106
+ createdAt: t.optional(t.text())
107
+ }),
108
+ maxSize: 50, // Process up to 50 users at once
109
+ maxDuration: [5, "seconds"], // Or flush every 5 seconds
110
+ handler: async (users) => {
111
+ // Bulk insert users - much faster than individual inserts
112
+ console.log(`Processing batch of ${users.length} users`);
113
+
114
+ const result = await this.database.users.insertMany(users.map(user => ({
115
+ ...user,
116
+ createdAt: user.createdAt || new Date().toISOString()
117
+ })));
118
+
119
+ console.log(`Successfully inserted ${result.length} users`);
120
+ return { inserted: result.length, userIds: result.map(r => r.id) };
121
+ }
122
+ });
123
+
124
+ async createUser(userData: { name: string; email: string }) {
125
+ // Individual calls are automatically batched
126
+ const result = await this.userBatch.push({
127
+ id: generateId(),
128
+ name: userData.name,
129
+ email: userData.email
130
+ });
131
+
132
+ return result; // Returns the batch result once batch is processed
133
+ }
134
+ }
135
+ ```
136
+
137
+ **API call batching with partitioning:**
138
+ ```ts
139
+ class NotificationService {
140
+ notificationBatch = $batch({
141
+ schema: t.object({
142
+ userId: t.text(),
143
+ type: t.enum(["email", "sms", "push"]),
144
+ message: t.text(),
145
+ priority: t.enum(["high", "normal", "low"])
146
+ }),
147
+ maxSize: 100,
148
+ maxDuration: [10, "seconds"],
149
+ // Partition by notification type for different processing
150
+ partitionBy: (notification) => notification.type,
151
+ concurrency: 3, // Process up to 3 different types simultaneously
152
+ handler: async (notifications) => {
153
+ const type = notifications[0].type; // All items in batch have same type
154
+ console.log(`Processing ${notifications.length} ${type} notifications`);
155
+
156
+ switch (type) {
157
+ case 'email':
158
+ return await this.emailProvider.sendBulk(notifications.map(n => ({
159
+ to: n.userId,
160
+ subject: 'Notification',
161
+ body: n.message,
162
+ priority: n.priority
163
+ })));
164
+
165
+ case 'sms':
166
+ return await this.smsProvider.sendBulk(notifications.map(n => ({
167
+ to: n.userId,
168
+ message: n.message
169
+ })));
170
+
171
+ case 'push':
172
+ return await this.pushProvider.sendBulk(notifications.map(n => ({
173
+ userId: n.userId,
174
+ title: 'Notification',
175
+ body: n.message,
176
+ priority: n.priority
177
+ })));
178
+ }
179
+ }
180
+ });
181
+
182
+ async sendNotification(userId: string, type: 'email' | 'sms' | 'push', message: string, priority: 'high' | 'normal' | 'low' = 'normal') {
183
+ // Notifications are automatically batched by type
184
+ return await this.notificationBatch.push({
185
+ userId,
186
+ type,
187
+ message,
188
+ priority
189
+ });
190
+ }
191
+ }
192
+ ```
193
+
194
+ **Log aggregation with retry logic:**
195
+ ```ts
196
+ class LoggingService {
197
+ logBatch = $batch({
198
+ schema: t.object({
199
+ timestamp: t.number(),
200
+ level: t.enum(["info", "warn", "error"]),
201
+ message: t.text(),
202
+ metadata: t.optional(t.record(t.text(), t.any())),
203
+ source: t.text()
204
+ }),
205
+ maxSize: 1000, // Large batches for log efficiency
206
+ maxDuration: [30, "seconds"], // Longer duration for log aggregation
207
+ concurrency: 2, // Limit concurrent log shipments
208
+ retry: {
209
+ maxAttempts: 5,
210
+ delay: [2, "seconds"],
211
+ backoff: "exponential"
212
+ },
213
+ handler: async (logEntries) => {
214
+ console.log(`Shipping ${logEntries.length} log entries`);
215
+
216
+ try {
217
+ // Ship logs to external service (e.g., Elasticsearch, Splunk)
218
+ const response = await this.logShipper.bulkIndex({
219
+ index: 'application-logs',
220
+ body: logEntries.map(entry => ([
221
+ { index: { _index: 'application-logs' } },
222
+ {
223
+ ...entry,
224
+ '@timestamp': new Date(entry.timestamp).toISOString()
225
+ }
226
+ ])).flat()
227
+ });
228
+
229
+ if (response.errors) {
230
+ console.error(`Some log entries failed to index`, response.errors);
231
+ // Retry will be triggered by throwing
232
+ throw new Error(`Failed to index ${response.errors.length} log entries`);
233
+ }
234
+
235
+ console.log(`Successfully shipped ${logEntries.length} log entries`);
236
+ return { shipped: logEntries.length, indexedAt: Date.now() };
237
+
238
+ } catch (error) {
239
+ console.error(`Failed to ship logs batch`, error);
240
+ throw error; // Trigger retry mechanism
241
+ }
242
+ }
243
+ });
244
+
245
+ async log(level: 'info' | 'warn' | 'error', message: string, metadata?: Record<string, any>, source: string = 'application') {
246
+ // Individual log calls are batched and shipped efficiently
247
+ return await this.logBatch.push({
248
+ timestamp: Date.now(),
249
+ level,
250
+ message,
251
+ metadata,
252
+ source
253
+ });
254
+ }
255
+ }
256
+ ```
257
+
258
+ **File processing with dynamic partitioning:**
259
+ ```ts
260
+ class FileProcessingService {
261
+ fileProcessingBatch = $batch({
262
+ schema: t.object({
263
+ filePath: t.text(),
264
+ fileType: t.enum(["image", "video", "document"]),
265
+ processingOptions: t.object({
266
+ quality: t.optional(t.enum(["low", "medium", "high"])),
267
+ format: t.optional(t.text()),
268
+ compress: t.optional(t.boolean())
269
+ }),
270
+ priority: t.enum(["urgent", "normal", "background"])
271
+ }),
272
+ maxSize: 20, // Smaller batches for file processing
273
+ maxDuration: [2, "minutes"], // Reasonable time for file accumulation
274
+ // Partition by file type and priority for optimal resource usage
275
+ partitionBy: (file) => `${file.fileType}-${file.priority}`,
276
+ concurrency: 4, // Multiple concurrent processing pipelines
277
+ retry: {
278
+ maxAttempts: 3,
279
+ delay: [5, "seconds"]
280
+ },
281
+ handler: async (files) => {
282
+ const fileType = files[0].fileType;
283
+ const priority = files[0].priority;
284
+
285
+ console.log(`Processing ${files.length} ${fileType} files with ${priority} priority`);
286
+
287
+ try {
288
+ const results = [];
289
+
290
+ for (const file of files) {
291
+ const result = await this.processFile(file.filePath, file.fileType, file.processingOptions);
292
+ results.push({
293
+ originalPath: file.filePath,
294
+ processedPath: result.outputPath,
295
+ size: result.size,
296
+ duration: result.processingTime
297
+ });
298
+ }
299
+
300
+ // Update database with batch results
301
+ await this.updateProcessingStatus(results);
302
+
303
+ console.log(`Successfully processed ${files.length} ${fileType} files`);
304
+ return {
305
+ processed: files.length,
306
+ fileType,
307
+ priority,
308
+ totalSize: results.reduce((sum, r) => sum + r.size, 0),
309
+ results
310
+ };
311
+
312
+ } catch (error) {
313
+ console.error(`Batch file processing failed for ${fileType} files`, error);
314
+ throw error;
315
+ }
316
+ }
317
+ });
318
+
319
+ async processFile(filePath: string, fileType: 'image' | 'video' | 'document', options: any, priority: 'urgent' | 'normal' | 'background' = 'normal') {
320
+ // Files are automatically batched by type and priority
321
+ return await this.fileProcessingBatch.push({
322
+ filePath,
323
+ fileType,
324
+ processingOptions: options,
325
+ priority
326
+ });
327
+ }
328
+ }
329
+ ```
@@ -0,0 +1,100 @@
1
+ import * as _alepha_core0 from "@alepha/core";
2
+ import { RouterGoOptions, UseActiveOptions } from "@alepha/react";
3
+ import "@mantine/core/styles.css";
4
+ import "@mantine/nprogress/styles.css";
5
+ import "@mantine/spotlight/styles.css";
6
+ import "@mantine/notifications/styles.css";
7
+ import { FormModel, InputField } from "@alepha/react-form";
8
+ import { AutocompleteProps, ButtonProps, ColorSchemeScriptProps, MantineProviderProps, PasswordInputProps, SegmentedControlProps, SelectProps, SwitchProps, TextInputProps, TextareaProps } from "@mantine/core";
9
+ import { ComponentType, ReactNode } from "react";
10
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
11
+ import { ModalsProviderProps } from "@mantine/modals";
12
+ import { NotificationsProps } from "@mantine/notifications";
13
+ import { NavigationProgressProps } from "@mantine/nprogress";
14
+
15
+ //#region src/components/Action.d.ts
16
+ interface ActionCommonProps extends ButtonProps {
17
+ children?: ReactNode;
18
+ textVisibleFrom?: "xs" | "sm" | "md" | "lg" | "xl";
19
+ /**
20
+ * If set, a confirmation dialog will be shown before performing the action.
21
+ * If `true`, a default title and message will be used.
22
+ * If a string, it will be used as the message with a default title.
23
+ * If an object, it can contain `title` and `message` properties to customize the dialog.
24
+ */
25
+ confirm?: boolean | string | {
26
+ title?: string;
27
+ message: string;
28
+ };
29
+ }
30
+ type ActionProps = ActionCommonProps & (ActiveHrefProps | ActionClickProps | ActionSubmitProps | {});
31
+ declare const Action: (_props: ActionProps) => react_jsx_runtime0.JSX.Element;
32
+ interface ActionSubmitProps extends ButtonProps {
33
+ form: FormModel<any>;
34
+ }
35
+ interface ActionClickProps extends ButtonProps {
36
+ onClick: (e: any) => any;
37
+ }
38
+ interface ActiveHrefProps extends ButtonProps {
39
+ href: string;
40
+ active?: Partial<UseActiveOptions> | false;
41
+ routerGoOptions?: RouterGoOptions;
42
+ }
43
+ //#endregion
44
+ //#region src/components/AlephaMantineProvider.d.ts
45
+ interface AlephaMantineProviderProps {
46
+ children?: ReactNode;
47
+ mantine?: MantineProviderProps;
48
+ colorSchemeScript?: ColorSchemeScriptProps;
49
+ navigationProgress?: NavigationProgressProps;
50
+ notifications?: NotificationsProps;
51
+ modals?: ModalsProviderProps;
52
+ }
53
+ declare const AlephaMantineProvider: (props: AlephaMantineProviderProps) => react_jsx_runtime0.JSX.Element;
54
+ //#endregion
55
+ //#region src/components/Control.d.ts
56
+ interface ControlProps {
57
+ input: InputField;
58
+ title?: string;
59
+ description?: string;
60
+ icon?: ReactNode;
61
+ text?: TextInputProps;
62
+ area?: boolean | TextareaProps;
63
+ select?: boolean | SelectProps;
64
+ autocomplete?: boolean | AutocompleteProps;
65
+ password?: boolean | PasswordInputProps;
66
+ switch?: boolean | SwitchProps;
67
+ segmented?: boolean | Partial<SegmentedControlProps>;
68
+ custom?: ComponentType<CustomControlProps>;
69
+ }
70
+ /**
71
+ * Generic form control that renders the appropriate input based on the schema and props.
72
+ *
73
+ * Supports:
74
+ * - TextInput
75
+ * - Textarea
76
+ * - Select (for enum types)
77
+ * - Autocomplete
78
+ * - PasswordInput
79
+ * - Switch (for boolean types)
80
+ * - SegmentedControl (for enum types)
81
+ * - Custom component
82
+ *
83
+ * Automatically handles labels, descriptions, error messages, and required state.
84
+ */
85
+ declare const Control: (props: ControlProps) => react_jsx_runtime0.JSX.Element | null;
86
+ type CustomControlProps = {
87
+ defaultValue: any;
88
+ onChange: (value: any) => void;
89
+ };
90
+ //#endregion
91
+ //#region src/index.d.ts
92
+ /**
93
+ *
94
+ *
95
+ * @module alepha.ui
96
+ */
97
+ declare const AlephaUI: _alepha_core0.Service<_alepha_core0.Module<{}>>;
98
+ //#endregion
99
+ export { Action, AlephaMantineProvider, AlephaUI, Control };
100
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/components/Action.tsx","../src/components/AlephaMantineProvider.tsx","../src/components/Control.tsx","../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;UAWiB,iBAAA,SAA0B;aAC/B;;;;;;;;;;IADK,OAAA,EAAA,MAAA;EAAkB,CAAA;;AAAQ,KAc/B,WAAA,GAAc,iBAdiB,GAAA,CAezC,eAfyC,GAevB,gBAfuB,GAeJ,iBAfI,GAAA,CAAA,CAAA,CAAA;cAmBrC,MAnBgD,EAAA,CAAA,MAAA,EAmB9B,WAnB8B,EAAA,GAmBnB,kBAAA,CAAA,GAAA,CAAA,OAnBmB;AAc/B,UAkEN,iBAAA,SAA0B,WAlEpB,CAAA;MAAG,EAmEnB,SAnEmB,CAAA,GAAA,CAAA;;AACN,UAyFH,gBAAA,SAAyB,WAzFtB,CAAA;SAAmB,EAAA,CAAA,CAAA,EAAA,GAAA,EAAA,GAAA,GAAA;;AAIjC,UA6HW,eAAA,SAAwB,WAtExC,CAAA;EAAA,IAAA,EAAA,MAAA;QAvDuB,CAAA,EA+Hd,OA/Hc,CA+HN,gBA/HM,CAAA,GAAA,KAAA;iBAAW,CAAA,EAgIhB,eAhIgB;;;;UClBlB,0BAAA;aACL;YACD;sBACU;uBACC;kBACL;WACP;;cAGJ,+BAAgC,+BAA0B,kBAAA,CAAA,GAAA,CAAA;;;UCC/C,YAAA;SACT;;;SAKA;SAEA;mBACU;qBACE;2BACM;uBACJ;qBACF;wBACG,QAAQ;EFzBd,MAAA,CAAA,EE2BP,aF3ByB,CE2BX,kBF3BW,CAAA;;;;;AAcnC;;;;;;;AAC+D;;;;;AAiE/D,cEnCM,OFmCW,EAAA,CAAkB,KAAA,EEnCX,YFmCW,EAAA,GEnCC,kBAAA,CAAA,GAAA,CAAA,OAAA,GFmCD,IAAA;AAC5B,KEmLK,kBAAA,GFnLL;cADoC,EAAA,GAAA;EAAW,QAAA,EAAA,CAAA,KAAA,EAAA,GAAA,EAAA,GAAA,IAAA;AAwBtD,CAAA;;;;;;;;cG7Fa,UAAQ,aAAA,CAAA,QAGnB,aAAA,CAHmB"}