alepha 0.13.6 → 0.13.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-audits/index.browser.js +116 -0
- package/dist/api-audits/index.browser.js.map +1 -0
- package/dist/api-audits/index.d.ts +1194 -0
- package/dist/api-audits/index.js +674 -0
- package/dist/api-audits/index.js.map +1 -0
- package/dist/api-notifications/index.d.ts +147 -147
- package/dist/api-parameters/index.browser.js +36 -5
- package/dist/api-parameters/index.browser.js.map +1 -1
- package/dist/api-parameters/index.d.ts +711 -33
- package/dist/api-parameters/index.js +831 -17
- package/dist/api-parameters/index.js.map +1 -1
- package/dist/api-users/index.d.ts +793 -780
- package/dist/api-users/index.js +699 -19
- package/dist/api-users/index.js.map +1 -1
- package/dist/api-verifications/index.js +2 -1
- package/dist/api-verifications/index.js.map +1 -1
- package/dist/bin/index.js +1 -0
- package/dist/bin/index.js.map +1 -1
- package/dist/cli/index.d.ts +85 -31
- package/dist/cli/index.js +205 -33
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +67 -6
- package/dist/command/index.js +30 -3
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +241 -61
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +170 -90
- package/dist/core/index.js +264 -67
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +248 -65
- package/dist/core/index.native.js.map +1 -1
- package/dist/email/index.js +15 -10554
- package/dist/email/index.js.map +1 -1
- package/dist/logger/index.d.ts +4 -4
- package/dist/logger/index.js +77 -72
- package/dist/logger/index.js.map +1 -1
- package/dist/orm/index.d.ts +5 -1
- package/dist/orm/index.js +24 -7
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/index.d.ts +4 -4
- package/dist/scheduler/index.d.ts +6 -6
- package/dist/server/index.d.ts +10 -1
- package/dist/server/index.js +20 -6
- package/dist/server/index.js.map +1 -1
- package/dist/server-auth/index.d.ts +163 -152
- package/dist/server-auth/index.js +40 -10
- package/dist/server-auth/index.js.map +1 -1
- package/dist/server-cookies/index.js +5 -1
- package/dist/server-cookies/index.js.map +1 -1
- package/dist/server-links/index.d.ts +33 -33
- package/dist/server-security/index.d.ts +9 -9
- package/dist/thread/index.js +2 -2
- package/dist/thread/index.js.map +1 -1
- package/dist/vite/index.d.ts +2 -2
- package/dist/vite/index.js +102 -45
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.browser.js +3 -3
- package/dist/websocket/index.browser.js.map +1 -1
- package/dist/websocket/index.js +4 -4
- package/dist/websocket/index.js.map +1 -1
- package/package.json +14 -9
- package/src/api-audits/controllers/AuditController.ts +186 -0
- package/src/api-audits/entities/audits.ts +132 -0
- package/src/api-audits/index.browser.ts +18 -0
- package/src/api-audits/index.ts +58 -0
- package/src/api-audits/primitives/$audit.ts +159 -0
- package/src/api-audits/schemas/auditQuerySchema.ts +23 -0
- package/src/api-audits/schemas/auditResourceSchema.ts +9 -0
- package/src/api-audits/schemas/createAuditSchema.ts +27 -0
- package/src/api-audits/services/AuditService.ts +412 -0
- package/src/api-parameters/controllers/ConfigController.ts +324 -0
- package/src/api-parameters/entities/parameters.ts +93 -10
- package/src/api-parameters/index.ts +43 -4
- package/src/api-parameters/primitives/$config.ts +291 -19
- package/src/api-parameters/schedulers/ConfigActivationScheduler.ts +30 -0
- package/src/api-parameters/services/ConfigStore.ts +491 -0
- package/src/api-users/atoms/realmAuthSettingsAtom.ts +19 -0
- package/src/api-users/controllers/UserRealmController.ts +0 -2
- package/src/api-users/index.ts +2 -0
- package/src/api-users/primitives/$userRealm.ts +18 -3
- package/src/api-users/providers/UserRealmProvider.ts +6 -3
- package/src/api-users/services/RegistrationService.ts +2 -1
- package/src/api-users/services/SessionService.ts +4 -0
- package/src/api-users/services/UserService.ts +3 -0
- package/src/api-verifications/index.ts +7 -1
- package/src/bin/index.ts +1 -0
- package/src/cli/assets/biomeJson.ts +1 -1
- package/src/cli/assets/dummySpecTs.ts +7 -0
- package/src/cli/assets/editorconfig.ts +13 -0
- package/src/cli/assets/mainTs.ts +14 -0
- package/src/cli/commands/BiomeCommands.ts +2 -0
- package/src/cli/commands/CoreCommands.ts +28 -9
- package/src/cli/commands/VerifyCommands.ts +2 -1
- package/src/cli/commands/ViteCommands.ts +8 -9
- package/src/cli/services/AlephaCliUtils.ts +214 -23
- package/src/command/helpers/Asker.ts +0 -1
- package/src/command/primitives/$command.ts +67 -0
- package/src/command/providers/CliProvider.ts +39 -8
- package/src/core/Alepha.ts +40 -30
- package/src/core/helpers/jsonSchemaToTypeBox.ts +307 -0
- package/src/core/index.shared.ts +1 -0
- package/src/core/index.ts +30 -3
- package/src/core/providers/EventManager.ts +1 -1
- package/src/core/providers/StateManager.ts +23 -12
- package/src/core/providers/TypeProvider.ts +26 -34
- package/src/logger/index.ts +8 -6
- package/src/logger/primitives/$logger.ts +1 -1
- package/src/logger/providers/{SimpleFormatterProvider.ts → PrettyFormatterProvider.ts} +10 -1
- package/src/orm/index.ts +6 -0
- package/src/orm/services/PgRelationManager.ts +2 -2
- package/src/orm/services/PostgresModelBuilder.ts +11 -7
- package/src/orm/services/Repository.ts +16 -7
- package/src/orm/services/SqliteModelBuilder.ts +10 -0
- package/src/server/index.ts +6 -0
- package/src/server/primitives/$action.ts +10 -1
- package/src/server/providers/ServerBodyParserProvider.ts +11 -5
- package/src/server/providers/ServerRouterProvider.ts +13 -7
- package/src/server-auth/primitives/$auth.ts +7 -0
- package/src/server-auth/providers/ServerAuthProvider.ts +51 -8
- package/src/server-cookies/index.ts +2 -1
- package/src/thread/primitives/$thread.ts +2 -2
- package/src/vite/index.ts +0 -2
- package/src/vite/tasks/buildServer.ts +3 -4
- package/src/vite/tasks/generateCloudflare.ts +35 -19
- package/src/vite/tasks/generateDocker.ts +18 -4
- package/src/vite/tasks/generateSitemap.ts +5 -7
- package/src/vite/tasks/generateVercel.ts +76 -41
- package/src/vite/tasks/runAlepha.ts +16 -1
- package/src/websocket/providers/NodeWebSocketServerProvider.ts +3 -11
- package/src/websocket/services/WebSocketClient.ts +3 -3
- package/dist/cli/dist-BlfFtOk2.js +0 -2770
- package/dist/cli/dist-BlfFtOk2.js.map +0 -1
- package/src/api-parameters/controllers/ParameterController.ts +0 -45
- package/src/api-parameters/services/ParameterStore.ts +0 -23
|
@@ -169,8 +169,23 @@ export class CliProvider {
|
|
|
169
169
|
root: process.cwd(),
|
|
170
170
|
};
|
|
171
171
|
|
|
172
|
+
// Execute pre-hooks
|
|
173
|
+
const preHooks = this.findPreHooks(command.name);
|
|
174
|
+
for (const hook of preHooks) {
|
|
175
|
+
this.log.debug(`Executing pre-hook for '${command.name}'...`);
|
|
176
|
+
await hook.options.handler(args as CommandHandlerArgs<TObject>);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Execute main command
|
|
172
180
|
await command.options.handler(args as CommandHandlerArgs<TObject>);
|
|
173
181
|
|
|
182
|
+
// Execute post-hooks
|
|
183
|
+
const postHooks = this.findPostHooks(command.name);
|
|
184
|
+
for (const hook of postHooks) {
|
|
185
|
+
this.log.debug(`Executing post-hook for '${command.name}'...`);
|
|
186
|
+
await hook.options.handler(args as CommandHandlerArgs<TObject>);
|
|
187
|
+
}
|
|
188
|
+
|
|
174
189
|
if (command.options.summary !== false) {
|
|
175
190
|
runner.summary();
|
|
176
191
|
}
|
|
@@ -185,11 +200,25 @@ export class CliProvider {
|
|
|
185
200
|
}
|
|
186
201
|
|
|
187
202
|
protected findCommand(name: string): CommandPrimitive<TObject> | undefined {
|
|
188
|
-
return this.commands.
|
|
203
|
+
return this.commands.findLast(
|
|
189
204
|
(command) => command.name === name || command.aliases.includes(name),
|
|
190
205
|
);
|
|
191
206
|
}
|
|
192
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Find all pre-hooks for a command.
|
|
210
|
+
*/
|
|
211
|
+
protected findPreHooks(commandName: string): CommandPrimitive<TObject>[] {
|
|
212
|
+
return this.commands.filter((cmd) => cmd.name === `pre${commandName}`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Find all post-hooks for a command.
|
|
217
|
+
*/
|
|
218
|
+
protected findPostHooks(commandName: string): CommandPrimitive<TObject>[] {
|
|
219
|
+
return this.commands.filter((cmd) => cmd.name === `post${commandName}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
193
222
|
/**
|
|
194
223
|
* Get all global flags including those from the root command (name === "")
|
|
195
224
|
*/
|
|
@@ -464,8 +493,8 @@ export class CliProvider {
|
|
|
464
493
|
const maxCmdLength = this.getMaxCmdLength(this.commands);
|
|
465
494
|
|
|
466
495
|
for (const command of this.commands) {
|
|
467
|
-
// skip root command in list
|
|
468
|
-
if (command.name === "") {
|
|
496
|
+
// skip root command and hooks in list
|
|
497
|
+
if (command.name === "" || command.options.hide) {
|
|
469
498
|
continue;
|
|
470
499
|
}
|
|
471
500
|
|
|
@@ -495,11 +524,13 @@ export class CliProvider {
|
|
|
495
524
|
|
|
496
525
|
private getMaxCmdLength(commands: CommandPrimitive[]): number {
|
|
497
526
|
return Math.max(
|
|
498
|
-
...commands
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
527
|
+
...commands
|
|
528
|
+
.filter((c) => !c.options.hide && c.name !== "")
|
|
529
|
+
.map((c) => {
|
|
530
|
+
const cmdStr = [c.name, ...c.aliases].join(", ");
|
|
531
|
+
const argsUsage = this.generateArgsUsage(c.options.args);
|
|
532
|
+
return `${cmdStr}${argsUsage}`.length;
|
|
533
|
+
}),
|
|
503
534
|
);
|
|
504
535
|
}
|
|
505
536
|
|
package/src/core/Alepha.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Static, TObject } from "typebox";
|
|
1
|
+
import type { Record, Static, TObject } from "typebox";
|
|
2
2
|
import { KIND } from "./constants/KIND.ts";
|
|
3
3
|
import { MODULE } from "./constants/MODULE.ts";
|
|
4
4
|
import { OPTIONS } from "./constants/OPTIONS.ts";
|
|
@@ -165,6 +165,11 @@ export class Alepha {
|
|
|
165
165
|
...state.env,
|
|
166
166
|
...process.env,
|
|
167
167
|
};
|
|
168
|
+
|
|
169
|
+
// force production mode when building with vite
|
|
170
|
+
if (process.env.NODE_ENV === "production") {
|
|
171
|
+
(state.env as Record<string, string>).NODE_ENV ??= "production";
|
|
172
|
+
}
|
|
168
173
|
}
|
|
169
174
|
|
|
170
175
|
const alepha = new Alepha(state);
|
|
@@ -224,13 +229,6 @@ export class Alepha {
|
|
|
224
229
|
*/
|
|
225
230
|
protected starting?: PromiseWithResolvers<this>;
|
|
226
231
|
|
|
227
|
-
/**
|
|
228
|
-
* Initial state of the container.
|
|
229
|
-
*
|
|
230
|
-
* > Used to initialize the StateManager.
|
|
231
|
-
*/
|
|
232
|
-
protected init: Partial<State>;
|
|
233
|
-
|
|
234
232
|
/**
|
|
235
233
|
* During the instantiation process, we keep a list of pending instantiations.
|
|
236
234
|
* > It allows us to detect circular dependencies.
|
|
@@ -276,37 +274,24 @@ export class Alepha {
|
|
|
276
274
|
*
|
|
277
275
|
* Mocked for browser environments.
|
|
278
276
|
*/
|
|
279
|
-
public
|
|
280
|
-
return this.inject(AlsProvider);
|
|
281
|
-
}
|
|
277
|
+
public context: AlsProvider;
|
|
282
278
|
|
|
283
279
|
/**
|
|
284
280
|
* Event manager to handle lifecycle events and custom events.
|
|
285
281
|
*/
|
|
286
|
-
public
|
|
287
|
-
return this.inject(EventManager, {
|
|
288
|
-
args: [() => this.log],
|
|
289
|
-
});
|
|
290
|
-
}
|
|
282
|
+
public events: EventManager;
|
|
291
283
|
|
|
292
284
|
/**
|
|
293
285
|
* State manager to store arbitrary values.
|
|
294
286
|
*/
|
|
295
|
-
public
|
|
296
|
-
this.events; // ensure events is initialized first (TODO: move this to constructor?)
|
|
297
|
-
return this.inject(StateManager, {
|
|
298
|
-
args: [this.init],
|
|
299
|
-
});
|
|
300
|
-
}
|
|
287
|
+
public store: StateManager<State>;
|
|
301
288
|
|
|
302
289
|
/**
|
|
303
290
|
* Codec manager for encoding and decoding data with different formats.
|
|
304
291
|
*
|
|
305
292
|
* Supports multiple codec formats (JSON, Protobuf, etc.) with a unified interface.
|
|
306
293
|
*/
|
|
307
|
-
public
|
|
308
|
-
return this.inject(CodecManager);
|
|
309
|
-
}
|
|
294
|
+
public codec: CodecManager;
|
|
310
295
|
|
|
311
296
|
/**
|
|
312
297
|
* Get logger instance.
|
|
@@ -322,8 +307,14 @@ export class Alepha {
|
|
|
322
307
|
return this.store.get("env") ?? {};
|
|
323
308
|
}
|
|
324
309
|
|
|
325
|
-
constructor(
|
|
326
|
-
this.
|
|
310
|
+
constructor(state: Partial<State> = {}) {
|
|
311
|
+
this.store = this.inject(StateManager, {
|
|
312
|
+
args: [state],
|
|
313
|
+
});
|
|
314
|
+
this.events = this.inject(EventManager);
|
|
315
|
+
this.events.logFn = () => this.log;
|
|
316
|
+
this.context = this.inject(AlsProvider);
|
|
317
|
+
this.codec = this.inject(CodecManager);
|
|
327
318
|
}
|
|
328
319
|
|
|
329
320
|
public set<T extends TAtomObject>(
|
|
@@ -416,11 +407,16 @@ export class Alepha {
|
|
|
416
407
|
return false;
|
|
417
408
|
}
|
|
418
409
|
|
|
410
|
+
// Vercel support
|
|
419
411
|
if (this.env.VERCEL_REGION) {
|
|
420
412
|
return true;
|
|
421
413
|
}
|
|
422
414
|
|
|
423
|
-
|
|
415
|
+
// Cloudflare Workers support
|
|
416
|
+
if (
|
|
417
|
+
typeof global === "object" &&
|
|
418
|
+
typeof (global as any).Cloudflare === "object"
|
|
419
|
+
) {
|
|
424
420
|
return true;
|
|
425
421
|
}
|
|
426
422
|
|
|
@@ -471,8 +467,6 @@ export class Alepha {
|
|
|
471
467
|
return this.starting.promise;
|
|
472
468
|
}
|
|
473
469
|
|
|
474
|
-
this.codec; // ensure codec is initialized
|
|
475
|
-
|
|
476
470
|
this.starting = Promise.withResolvers();
|
|
477
471
|
|
|
478
472
|
const now = Date.now();
|
|
@@ -832,6 +826,22 @@ export class Alepha {
|
|
|
832
826
|
return config as Static<T>;
|
|
833
827
|
}
|
|
834
828
|
|
|
829
|
+
/**
|
|
830
|
+
* Get all environment variable schemas and their parsed values.
|
|
831
|
+
*
|
|
832
|
+
* This is useful for DevTools to display all expected environment variables.
|
|
833
|
+
*/
|
|
834
|
+
public getEnvSchemas(): Array<{
|
|
835
|
+
schema: TSchema;
|
|
836
|
+
values: Record<string, any>;
|
|
837
|
+
}> {
|
|
838
|
+
const result: Array<{ schema: TSchema; values: Record<string, any> }> = [];
|
|
839
|
+
for (const [schema, values] of this.cacheEnv.entries()) {
|
|
840
|
+
result.push({ schema, values });
|
|
841
|
+
}
|
|
842
|
+
return result;
|
|
843
|
+
}
|
|
844
|
+
|
|
835
845
|
// -------------------------------------------------------------------------------------------------------------------
|
|
836
846
|
|
|
837
847
|
/**
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
TArrayOptions,
|
|
3
|
+
TNumberOptions,
|
|
4
|
+
TObjectOptions,
|
|
5
|
+
TSchema,
|
|
6
|
+
TStringOptions,
|
|
7
|
+
} from "typebox";
|
|
8
|
+
import { t } from "../providers/TypeProvider.ts";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* JSON Schema representation for conversion to TypeBox.
|
|
12
|
+
*/
|
|
13
|
+
export interface JsonSchemaObject {
|
|
14
|
+
type?: string | string[];
|
|
15
|
+
properties?: Record<string, JsonSchemaObject>;
|
|
16
|
+
required?: string[];
|
|
17
|
+
items?: JsonSchemaObject;
|
|
18
|
+
enum?: (string | number | boolean)[];
|
|
19
|
+
const?: unknown;
|
|
20
|
+
format?: string;
|
|
21
|
+
title?: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
default?: unknown;
|
|
24
|
+
minLength?: number;
|
|
25
|
+
maxLength?: number;
|
|
26
|
+
minimum?: number;
|
|
27
|
+
maximum?: number;
|
|
28
|
+
exclusiveMinimum?: number;
|
|
29
|
+
exclusiveMaximum?: number;
|
|
30
|
+
multipleOf?: number;
|
|
31
|
+
pattern?: string;
|
|
32
|
+
minItems?: number;
|
|
33
|
+
maxItems?: number;
|
|
34
|
+
uniqueItems?: boolean;
|
|
35
|
+
// TypeBox internal markers (pass through)
|
|
36
|
+
"~kind"?: string;
|
|
37
|
+
// Not supported
|
|
38
|
+
oneOf?: JsonSchemaObject[];
|
|
39
|
+
anyOf?: JsonSchemaObject[];
|
|
40
|
+
allOf?: JsonSchemaObject[];
|
|
41
|
+
not?: JsonSchemaObject;
|
|
42
|
+
$ref?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Converts a JSON Schema object to a TypeBox schema using Alepha's type system.
|
|
47
|
+
*
|
|
48
|
+
* This is useful when receiving JSON Schema from an API (e.g., configuration schemas)
|
|
49
|
+
* and needing to use them with TypeBox-based form rendering or validation.
|
|
50
|
+
*
|
|
51
|
+
* **Supports:**
|
|
52
|
+
* - Basic types: string, number, integer, boolean, null, object, array
|
|
53
|
+
* - String formats: email, uuid, date-time, date, time, url/uri, binary, bigint, duration, color
|
|
54
|
+
* - Enums (string enums)
|
|
55
|
+
* - Nested objects with required/optional properties
|
|
56
|
+
* - Arrays with item schemas
|
|
57
|
+
* - Common validation options: minLength, maxLength, minimum, maximum, pattern
|
|
58
|
+
*
|
|
59
|
+
* **Not supported:**
|
|
60
|
+
* - oneOf, anyOf, allOf, not (composition schemas)
|
|
61
|
+
* - $ref (references)
|
|
62
|
+
* - additionalProperties, patternProperties
|
|
63
|
+
*
|
|
64
|
+
* @param schema - JSON Schema object to convert
|
|
65
|
+
* @returns TypeBox TSchema
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* const jsonSchema = {
|
|
70
|
+
* type: "object",
|
|
71
|
+
* properties: {
|
|
72
|
+
* email: { type: "string", format: "email" },
|
|
73
|
+
* age: { type: "integer", minimum: 0 },
|
|
74
|
+
* active: { type: "boolean" },
|
|
75
|
+
* },
|
|
76
|
+
* required: ["email"]
|
|
77
|
+
* };
|
|
78
|
+
*
|
|
79
|
+
* const typeBoxSchema = jsonSchemaToTypeBox(jsonSchema);
|
|
80
|
+
* // Equivalent to:
|
|
81
|
+
* // t.object({
|
|
82
|
+
* // email: t.email(),
|
|
83
|
+
* // age: t.optional(t.integer({ minimum: 0 })),
|
|
84
|
+
* // active: t.optional(t.boolean()),
|
|
85
|
+
* // })
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export function jsonSchemaToTypeBox(schema: JsonSchemaObject): TSchema {
|
|
89
|
+
// If it already has TypeBox marker, return as-is
|
|
90
|
+
if (schema["~kind"]) {
|
|
91
|
+
return schema as unknown as TSchema;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Handle const (literal)
|
|
95
|
+
if (schema.const !== undefined) {
|
|
96
|
+
return t.const(schema.const as string | number | boolean);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Handle enum
|
|
100
|
+
if (schema.enum && Array.isArray(schema.enum)) {
|
|
101
|
+
// String enum
|
|
102
|
+
if (schema.enum.every((v) => typeof v === "string")) {
|
|
103
|
+
return t.enum(
|
|
104
|
+
schema.enum as string[],
|
|
105
|
+
filterUndefined({
|
|
106
|
+
title: schema.title,
|
|
107
|
+
description: schema.description,
|
|
108
|
+
default: schema.default as string,
|
|
109
|
+
}),
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
// For non-string enums, use union of literals
|
|
113
|
+
return t.union(
|
|
114
|
+
schema.enum.map((v) => t.const(v as string | number | boolean)),
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Handle type
|
|
119
|
+
const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
|
|
120
|
+
|
|
121
|
+
switch (type) {
|
|
122
|
+
case "string":
|
|
123
|
+
return convertString(schema);
|
|
124
|
+
case "number":
|
|
125
|
+
return convertNumber(schema);
|
|
126
|
+
case "integer":
|
|
127
|
+
return convertInteger(schema);
|
|
128
|
+
case "boolean":
|
|
129
|
+
return convertBoolean(schema);
|
|
130
|
+
case "null":
|
|
131
|
+
return t.null();
|
|
132
|
+
case "object":
|
|
133
|
+
return convertObject(schema);
|
|
134
|
+
case "array":
|
|
135
|
+
return convertArray(schema);
|
|
136
|
+
default:
|
|
137
|
+
// If no type specified but has properties, treat as object
|
|
138
|
+
if (schema.properties) {
|
|
139
|
+
return convertObject(schema);
|
|
140
|
+
}
|
|
141
|
+
// Fallback to any
|
|
142
|
+
return t.any();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Remove undefined values from an object.
|
|
148
|
+
*/
|
|
149
|
+
function filterUndefined<T extends Record<string, unknown>>(
|
|
150
|
+
obj: T,
|
|
151
|
+
): Partial<T> {
|
|
152
|
+
return Object.fromEntries(
|
|
153
|
+
Object.entries(obj).filter(([_, v]) => v !== undefined),
|
|
154
|
+
) as Partial<T>;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Convert JSON Schema string type to TypeBox.
|
|
159
|
+
*/
|
|
160
|
+
function convertString(schema: JsonSchemaObject): TSchema {
|
|
161
|
+
const options: TStringOptions = filterUndefined({
|
|
162
|
+
title: schema.title,
|
|
163
|
+
description: schema.description,
|
|
164
|
+
default: schema.default as string,
|
|
165
|
+
minLength: schema.minLength,
|
|
166
|
+
maxLength: schema.maxLength,
|
|
167
|
+
pattern: schema.pattern,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
switch (schema.format) {
|
|
171
|
+
case "email":
|
|
172
|
+
return t.email(options);
|
|
173
|
+
case "uuid":
|
|
174
|
+
return t.uuid(options);
|
|
175
|
+
case "date-time":
|
|
176
|
+
return t.datetime(options);
|
|
177
|
+
case "date":
|
|
178
|
+
return t.date(options);
|
|
179
|
+
case "time":
|
|
180
|
+
return t.time(options);
|
|
181
|
+
case "url":
|
|
182
|
+
case "uri":
|
|
183
|
+
return t.url(options);
|
|
184
|
+
case "binary":
|
|
185
|
+
return t.binary(options);
|
|
186
|
+
case "bigint":
|
|
187
|
+
return t.bigint(options);
|
|
188
|
+
case "duration":
|
|
189
|
+
return t.duration(options);
|
|
190
|
+
case "color":
|
|
191
|
+
return t.text({ ...options, format: "color" });
|
|
192
|
+
case "e164":
|
|
193
|
+
return t.e164(options);
|
|
194
|
+
case "bcp47":
|
|
195
|
+
return t.bcp47(options);
|
|
196
|
+
default:
|
|
197
|
+
// For unknown formats, preserve the format in text
|
|
198
|
+
if (schema.format) {
|
|
199
|
+
return t.text({ ...options, format: schema.format });
|
|
200
|
+
}
|
|
201
|
+
return t.text(options);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Convert JSON Schema number type to TypeBox.
|
|
207
|
+
*/
|
|
208
|
+
function convertNumber(schema: JsonSchemaObject): TSchema {
|
|
209
|
+
return t.number(
|
|
210
|
+
filterUndefined({
|
|
211
|
+
title: schema.title,
|
|
212
|
+
description: schema.description,
|
|
213
|
+
default: schema.default as number,
|
|
214
|
+
minimum: schema.minimum,
|
|
215
|
+
maximum: schema.maximum,
|
|
216
|
+
exclusiveMinimum: schema.exclusiveMinimum,
|
|
217
|
+
exclusiveMaximum: schema.exclusiveMaximum,
|
|
218
|
+
multipleOf: schema.multipleOf,
|
|
219
|
+
}) as TNumberOptions,
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Convert JSON Schema integer type to TypeBox.
|
|
225
|
+
*/
|
|
226
|
+
function convertInteger(schema: JsonSchemaObject): TSchema {
|
|
227
|
+
return t.integer(
|
|
228
|
+
filterUndefined({
|
|
229
|
+
title: schema.title,
|
|
230
|
+
description: schema.description,
|
|
231
|
+
default: schema.default as number,
|
|
232
|
+
minimum: schema.minimum,
|
|
233
|
+
maximum: schema.maximum,
|
|
234
|
+
exclusiveMinimum: schema.exclusiveMinimum,
|
|
235
|
+
exclusiveMaximum: schema.exclusiveMaximum,
|
|
236
|
+
multipleOf: schema.multipleOf,
|
|
237
|
+
}) as TNumberOptions,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Convert JSON Schema boolean type to TypeBox.
|
|
243
|
+
*/
|
|
244
|
+
function convertBoolean(schema: JsonSchemaObject): TSchema {
|
|
245
|
+
return t.boolean(
|
|
246
|
+
filterUndefined({
|
|
247
|
+
title: schema.title,
|
|
248
|
+
description: schema.description,
|
|
249
|
+
default: schema.default as boolean,
|
|
250
|
+
}),
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Convert JSON Schema object type to TypeBox.
|
|
256
|
+
*/
|
|
257
|
+
function convertObject(schema: JsonSchemaObject): TSchema {
|
|
258
|
+
// No properties means it's a generic object/record
|
|
259
|
+
if (!schema.properties) {
|
|
260
|
+
return t.json(
|
|
261
|
+
filterUndefined({
|
|
262
|
+
title: schema.title,
|
|
263
|
+
description: schema.description,
|
|
264
|
+
}),
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const required = new Set(schema.required ?? []);
|
|
269
|
+
const properties: Record<string, TSchema> = {};
|
|
270
|
+
|
|
271
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
272
|
+
let converted = jsonSchemaToTypeBox(propSchema);
|
|
273
|
+
|
|
274
|
+
// Wrap in optional if not required
|
|
275
|
+
if (!required.has(key)) {
|
|
276
|
+
converted = t.optional(converted);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
properties[key] = converted;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return t.object(
|
|
283
|
+
properties,
|
|
284
|
+
filterUndefined({
|
|
285
|
+
title: schema.title,
|
|
286
|
+
description: schema.description,
|
|
287
|
+
}) as TObjectOptions,
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Convert JSON Schema array type to TypeBox.
|
|
293
|
+
*/
|
|
294
|
+
function convertArray(schema: JsonSchemaObject): TSchema {
|
|
295
|
+
const itemSchema = schema.items ? jsonSchemaToTypeBox(schema.items) : t.any();
|
|
296
|
+
|
|
297
|
+
return t.array(
|
|
298
|
+
itemSchema,
|
|
299
|
+
filterUndefined({
|
|
300
|
+
title: schema.title,
|
|
301
|
+
description: schema.description,
|
|
302
|
+
minItems: schema.minItems,
|
|
303
|
+
maxItems: schema.maxItems,
|
|
304
|
+
uniqueItems: schema.uniqueItems,
|
|
305
|
+
}) as TArrayOptions,
|
|
306
|
+
);
|
|
307
|
+
}
|
package/src/core/index.shared.ts
CHANGED
|
@@ -11,6 +11,7 @@ export * from "./errors/TooLateSubstitutionError.ts";
|
|
|
11
11
|
export * from "./errors/TypeBoxError.ts";
|
|
12
12
|
export * from "./helpers/createPagination.ts";
|
|
13
13
|
export * from "./helpers/FileLike.ts";
|
|
14
|
+
export * from "./helpers/jsonSchemaToTypeBox.ts";
|
|
14
15
|
export * from "./helpers/primitive.ts";
|
|
15
16
|
export * from "./interfaces/Async.ts";
|
|
16
17
|
export * from "./interfaces/LoggerInterface.ts";
|
package/src/core/index.ts
CHANGED
|
@@ -5,7 +5,15 @@ import { cpus } from "node:os";
|
|
|
5
5
|
import { Alepha } from "./Alepha.ts";
|
|
6
6
|
import type { RunOptions } from "./interfaces/Run.ts";
|
|
7
7
|
import type { Service } from "./interfaces/Service.ts";
|
|
8
|
+
import { $module } from "./primitives/$module.ts";
|
|
8
9
|
import { AlsProvider } from "./providers/AlsProvider.ts";
|
|
10
|
+
import { CodecManager } from "./providers/CodecManager.ts";
|
|
11
|
+
import { EventManager } from "./providers/EventManager.ts";
|
|
12
|
+
import { Json } from "./providers/Json.ts";
|
|
13
|
+
import { JsonSchemaCodec } from "./providers/JsonSchemaCodec.ts";
|
|
14
|
+
import { SchemaCodec } from "./providers/SchemaCodec.ts";
|
|
15
|
+
import { SchemaValidator } from "./providers/SchemaValidator.ts";
|
|
16
|
+
import { StateManager } from "./providers/StateManager.ts";
|
|
9
17
|
|
|
10
18
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
11
19
|
|
|
@@ -13,6 +21,25 @@ export * from "./index.shared.ts";
|
|
|
13
21
|
|
|
14
22
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
15
23
|
|
|
24
|
+
export const AlephaCore = $module({
|
|
25
|
+
name: "alepha.core",
|
|
26
|
+
services: [
|
|
27
|
+
StateManager,
|
|
28
|
+
CodecManager,
|
|
29
|
+
EventManager,
|
|
30
|
+
AlsProvider,
|
|
31
|
+
Json,
|
|
32
|
+
JsonSchemaCodec,
|
|
33
|
+
SchemaCodec,
|
|
34
|
+
SchemaValidator,
|
|
35
|
+
],
|
|
36
|
+
register: () => {
|
|
37
|
+
// skip registration, Alepha will handle it
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
42
|
+
|
|
16
43
|
/**
|
|
17
44
|
* Run Alepha application, trigger start lifecycle.
|
|
18
45
|
*
|
|
@@ -50,6 +77,7 @@ export const run = (
|
|
|
50
77
|
// it's not recommended, we should force 'export default run(alepha)'
|
|
51
78
|
(globalThis as any).__alepha = alepha;
|
|
52
79
|
|
|
80
|
+
// when alepha instance is imported via CLI, use a different global variable
|
|
53
81
|
if (env.ALEPHA_CLI_IMPORT) {
|
|
54
82
|
(globalThis as any).__cli_alepha = alepha;
|
|
55
83
|
}
|
|
@@ -63,8 +91,7 @@ export const run = (
|
|
|
63
91
|
return alepha;
|
|
64
92
|
}
|
|
65
93
|
|
|
66
|
-
|
|
67
|
-
(async () => {
|
|
94
|
+
setTimeout(async () => {
|
|
68
95
|
try {
|
|
69
96
|
await opts?.configure?.(alepha);
|
|
70
97
|
|
|
@@ -106,7 +133,7 @@ export const run = (
|
|
|
106
133
|
process.exit(1);
|
|
107
134
|
}
|
|
108
135
|
}
|
|
109
|
-
})
|
|
136
|
+
});
|
|
110
137
|
|
|
111
138
|
return alepha;
|
|
112
139
|
};
|
|
@@ -4,7 +4,7 @@ import type { Async } from "../interfaces/Async.ts";
|
|
|
4
4
|
import type { LoggerInterface } from "../interfaces/LoggerInterface.ts";
|
|
5
5
|
|
|
6
6
|
export class EventManager {
|
|
7
|
-
|
|
7
|
+
public logFn?: () => LoggerInterface | undefined;
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* List of events that can be triggered. Powered by $hook().
|
|
@@ -54,7 +54,9 @@ export class StateManager<State extends object = AlephaState> {
|
|
|
54
54
|
if (!this.atoms.has(key)) {
|
|
55
55
|
this.atoms.set(key, atom);
|
|
56
56
|
if (!(key in this.store)) {
|
|
57
|
-
this.set(key, atom.options.default as State[keyof State]
|
|
57
|
+
this.set(key, atom.options.default as State[keyof State], {
|
|
58
|
+
skipContext: true,
|
|
59
|
+
});
|
|
58
60
|
}
|
|
59
61
|
}
|
|
60
62
|
|
|
@@ -85,12 +87,14 @@ export class StateManager<State extends object = AlephaState> {
|
|
|
85
87
|
public set<T extends TAtomObject>(
|
|
86
88
|
target: Atom<T>,
|
|
87
89
|
value: AtomStatic<T>,
|
|
90
|
+
options?: SetStateOptions,
|
|
88
91
|
): this;
|
|
89
92
|
public set<Key extends keyof State>(
|
|
90
93
|
target: Key,
|
|
91
94
|
value: State[Key] | undefined,
|
|
95
|
+
options?: SetStateOptions,
|
|
92
96
|
): this;
|
|
93
|
-
public set(target: any, value: any): this {
|
|
97
|
+
public set(target: any, value: any, options?: SetStateOptions): this {
|
|
94
98
|
if (target instanceof Atom) {
|
|
95
99
|
this.register(target);
|
|
96
100
|
}
|
|
@@ -103,19 +107,21 @@ export class StateManager<State extends object = AlephaState> {
|
|
|
103
107
|
return this;
|
|
104
108
|
}
|
|
105
109
|
|
|
106
|
-
if (this.als?.exists()) {
|
|
110
|
+
if (options?.skipContext !== true && this.als?.exists()) {
|
|
107
111
|
this.als.set(key as string, value);
|
|
108
112
|
} else {
|
|
109
113
|
store[key] = value;
|
|
110
114
|
}
|
|
111
115
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
if (options?.skipEvents !== true) {
|
|
117
|
+
this.events
|
|
118
|
+
?.emit(
|
|
119
|
+
"state:mutate",
|
|
120
|
+
{ key: key as keyof AlephaState, value, prevValue },
|
|
121
|
+
{ catch: true },
|
|
122
|
+
)
|
|
123
|
+
.catch(() => null);
|
|
124
|
+
}
|
|
119
125
|
|
|
120
126
|
return this;
|
|
121
127
|
}
|
|
@@ -156,11 +162,11 @@ export class StateManager<State extends object = AlephaState> {
|
|
|
156
162
|
*/
|
|
157
163
|
public push<Key extends keyof OnlyArray<State>>(
|
|
158
164
|
key: Key,
|
|
159
|
-
value: NonNullable<State[Key]> extends Array<infer U> ? U : never
|
|
165
|
+
...value: Array<NonNullable<State[Key]> extends Array<infer U> ? U : never>
|
|
160
166
|
): this {
|
|
161
167
|
const current = (this.get(key) ?? []) as Array<any>; // default to empty array
|
|
162
168
|
if (Array.isArray(current)) {
|
|
163
|
-
this.set(key, [...current, value] as State[Key]);
|
|
169
|
+
this.set(key, [...current, ...value] as State[Key]);
|
|
164
170
|
}
|
|
165
171
|
return this;
|
|
166
172
|
}
|
|
@@ -184,3 +190,8 @@ export class StateManager<State extends object = AlephaState> {
|
|
|
184
190
|
type OnlyArray<T extends object> = {
|
|
185
191
|
[K in keyof T]: NonNullable<T[K]> extends Array<any> ? K : never;
|
|
186
192
|
};
|
|
193
|
+
|
|
194
|
+
export interface SetStateOptions {
|
|
195
|
+
skipContext?: boolean;
|
|
196
|
+
skipEvents?: boolean;
|
|
197
|
+
}
|