alepha 0.13.5 → 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.
Files changed (136) hide show
  1. package/dist/api-audits/index.browser.js +116 -0
  2. package/dist/api-audits/index.browser.js.map +1 -0
  3. package/dist/api-audits/index.d.ts +1194 -0
  4. package/dist/api-audits/index.js +674 -0
  5. package/dist/api-audits/index.js.map +1 -0
  6. package/dist/api-notifications/index.d.ts +147 -147
  7. package/dist/api-parameters/index.browser.js +36 -5
  8. package/dist/api-parameters/index.browser.js.map +1 -1
  9. package/dist/api-parameters/index.d.ts +711 -33
  10. package/dist/api-parameters/index.js +831 -17
  11. package/dist/api-parameters/index.js.map +1 -1
  12. package/dist/api-users/index.d.ts +16 -3
  13. package/dist/api-users/index.js +699 -19
  14. package/dist/api-users/index.js.map +1 -1
  15. package/dist/api-verifications/index.js +2 -1
  16. package/dist/api-verifications/index.js.map +1 -1
  17. package/dist/bin/index.js +1 -0
  18. package/dist/bin/index.js.map +1 -1
  19. package/dist/cli/index.d.ts +85 -31
  20. package/dist/cli/index.js +205 -33
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/command/index.d.ts +67 -6
  23. package/dist/command/index.js +30 -3
  24. package/dist/command/index.js.map +1 -1
  25. package/dist/core/index.browser.js +241 -61
  26. package/dist/core/index.browser.js.map +1 -1
  27. package/dist/core/index.d.ts +170 -90
  28. package/dist/core/index.js +264 -67
  29. package/dist/core/index.js.map +1 -1
  30. package/dist/core/index.native.js +248 -65
  31. package/dist/core/index.native.js.map +1 -1
  32. package/dist/email/index.js +15 -10554
  33. package/dist/email/index.js.map +1 -1
  34. package/dist/logger/index.d.ts +4 -4
  35. package/dist/logger/index.js +77 -72
  36. package/dist/logger/index.js.map +1 -1
  37. package/dist/orm/index.d.ts +5 -1
  38. package/dist/orm/index.js +24 -7
  39. package/dist/orm/index.js.map +1 -1
  40. package/dist/queue/index.d.ts +4 -4
  41. package/dist/redis/index.d.ts +10 -10
  42. package/dist/security/index.d.ts +28 -28
  43. package/dist/server/index.d.ts +10 -1
  44. package/dist/server/index.js +20 -6
  45. package/dist/server/index.js.map +1 -1
  46. package/dist/server-auth/index.d.ts +163 -152
  47. package/dist/server-auth/index.js +40 -10
  48. package/dist/server-auth/index.js.map +1 -1
  49. package/dist/server-cookies/index.js +5 -1
  50. package/dist/server-cookies/index.js.map +1 -1
  51. package/dist/server-links/index.d.ts +33 -33
  52. package/dist/server-security/index.d.ts +9 -9
  53. package/dist/thread/index.js +2 -2
  54. package/dist/thread/index.js.map +1 -1
  55. package/dist/vite/index.d.ts +2 -2
  56. package/dist/vite/index.js +102 -45
  57. package/dist/vite/index.js.map +1 -1
  58. package/dist/websocket/index.browser.js +3 -3
  59. package/dist/websocket/index.browser.js.map +1 -1
  60. package/dist/websocket/index.d.ts +7 -7
  61. package/dist/websocket/index.js +4 -4
  62. package/dist/websocket/index.js.map +1 -1
  63. package/package.json +14 -9
  64. package/src/api-audits/controllers/AuditController.ts +186 -0
  65. package/src/api-audits/entities/audits.ts +132 -0
  66. package/src/api-audits/index.browser.ts +18 -0
  67. package/src/api-audits/index.ts +58 -0
  68. package/src/api-audits/primitives/$audit.ts +159 -0
  69. package/src/api-audits/schemas/auditQuerySchema.ts +23 -0
  70. package/src/api-audits/schemas/auditResourceSchema.ts +9 -0
  71. package/src/api-audits/schemas/createAuditSchema.ts +27 -0
  72. package/src/api-audits/services/AuditService.ts +412 -0
  73. package/src/api-parameters/controllers/ConfigController.ts +324 -0
  74. package/src/api-parameters/entities/parameters.ts +93 -10
  75. package/src/api-parameters/index.ts +43 -4
  76. package/src/api-parameters/primitives/$config.ts +291 -19
  77. package/src/api-parameters/schedulers/ConfigActivationScheduler.ts +30 -0
  78. package/src/api-parameters/services/ConfigStore.ts +491 -0
  79. package/src/api-users/atoms/realmAuthSettingsAtom.ts +19 -0
  80. package/src/api-users/controllers/UserRealmController.ts +0 -2
  81. package/src/api-users/index.ts +2 -0
  82. package/src/api-users/primitives/$userRealm.ts +18 -3
  83. package/src/api-users/providers/UserRealmProvider.ts +6 -3
  84. package/src/api-users/services/RegistrationService.ts +2 -1
  85. package/src/api-users/services/SessionService.ts +4 -0
  86. package/src/api-users/services/UserService.ts +3 -0
  87. package/src/api-verifications/index.ts +7 -1
  88. package/src/bin/index.ts +1 -0
  89. package/src/cli/assets/biomeJson.ts +1 -1
  90. package/src/cli/assets/dummySpecTs.ts +7 -0
  91. package/src/cli/assets/editorconfig.ts +13 -0
  92. package/src/cli/assets/mainTs.ts +14 -0
  93. package/src/cli/commands/BiomeCommands.ts +2 -0
  94. package/src/cli/commands/CoreCommands.ts +28 -9
  95. package/src/cli/commands/VerifyCommands.ts +2 -1
  96. package/src/cli/commands/ViteCommands.ts +8 -9
  97. package/src/cli/services/AlephaCliUtils.ts +214 -23
  98. package/src/command/helpers/Asker.ts +0 -1
  99. package/src/command/primitives/$command.ts +67 -0
  100. package/src/command/providers/CliProvider.ts +39 -8
  101. package/src/core/Alepha.ts +40 -30
  102. package/src/core/helpers/jsonSchemaToTypeBox.ts +307 -0
  103. package/src/core/index.shared.ts +1 -0
  104. package/src/core/index.ts +30 -3
  105. package/src/core/providers/EventManager.ts +1 -1
  106. package/src/core/providers/StateManager.ts +23 -12
  107. package/src/core/providers/TypeProvider.ts +26 -34
  108. package/src/logger/index.ts +8 -6
  109. package/src/logger/primitives/$logger.ts +1 -1
  110. package/src/logger/providers/{SimpleFormatterProvider.ts → PrettyFormatterProvider.ts} +10 -1
  111. package/src/orm/index.ts +6 -0
  112. package/src/orm/services/PgRelationManager.ts +2 -2
  113. package/src/orm/services/PostgresModelBuilder.ts +11 -7
  114. package/src/orm/services/Repository.ts +16 -7
  115. package/src/orm/services/SqliteModelBuilder.ts +10 -0
  116. package/src/server/index.ts +6 -0
  117. package/src/server/primitives/$action.ts +10 -1
  118. package/src/server/providers/ServerBodyParserProvider.ts +11 -5
  119. package/src/server/providers/ServerRouterProvider.ts +13 -7
  120. package/src/server-auth/primitives/$auth.ts +7 -0
  121. package/src/server-auth/providers/ServerAuthProvider.ts +51 -8
  122. package/src/server-cookies/index.ts +2 -1
  123. package/src/thread/primitives/$thread.ts +2 -2
  124. package/src/vite/index.ts +0 -2
  125. package/src/vite/tasks/buildServer.ts +3 -4
  126. package/src/vite/tasks/generateCloudflare.ts +35 -19
  127. package/src/vite/tasks/generateDocker.ts +18 -4
  128. package/src/vite/tasks/generateSitemap.ts +5 -7
  129. package/src/vite/tasks/generateVercel.ts +76 -41
  130. package/src/vite/tasks/runAlepha.ts +16 -1
  131. package/src/websocket/providers/NodeWebSocketServerProvider.ts +3 -11
  132. package/src/websocket/services/WebSocketClient.ts +3 -3
  133. package/dist/cli/dist-BlfFtOk2.js +0 -2770
  134. package/dist/cli/dist-BlfFtOk2.js.map +0 -1
  135. package/src/api-parameters/controllers/ParameterController.ts +0 -45
  136. package/src/api-parameters/services/ParameterStore.ts +0 -23
@@ -85,6 +85,7 @@ export async function buildServer(
85
85
  noExternal: true,
86
86
  },
87
87
  build: {
88
+ sourcemap: true, // or "hidden" if you don't want to expose source maps
88
89
  ssr: opts.entry,
89
90
  outDir: `${opts.distDir}/server`,
90
91
  minify: true,
@@ -133,7 +134,7 @@ export async function buildServer(
133
134
  `${opts.distDir}/${opts.clientDir}/index.html`,
134
135
  "utf-8",
135
136
  );
136
- template = `process.env.REACT_SERVER_TEMPLATE ??= \`${index.replace(/>\s*</g, "><").trim()}\`;\n`;
137
+ template = `__alepha.set("alepha.react.server.template", \`${index.replace(/>\s*</g, "><").trim()}\`);\n`;
137
138
  }
138
139
 
139
140
  const warning =
@@ -141,11 +142,9 @@ export async function buildServer(
141
142
  "\n" +
142
143
  "// Changes to this file will be lost when the code is regenerated.\n";
143
144
 
144
- const forceProduction = "process.env.NODE_ENV ??= 'production';\n";
145
-
146
145
  await writeFile(
147
146
  `${opts.distDir}/index.js`,
148
- `${warning}\n${forceProduction}${template}\nawait import('./server/${entryFile}');`.trim(),
147
+ `${warning}\nimport './server/${entryFile}';${template}`.trim(),
149
148
  );
150
149
 
151
150
  return { entryFile };
@@ -1,5 +1,5 @@
1
- import { writeFileSync } from "node:fs";
2
- import { join } from "node:path";
1
+ import { writeFile } from "node:fs/promises";
2
+ import { basename, join } from "node:path";
3
3
 
4
4
  export interface GenerateCloudflareOptions {
5
5
  /**
@@ -24,43 +24,59 @@ const WARNING_COMMENT =
24
24
  export async function generateCloudflare(
25
25
  opts: GenerateCloudflareOptions = {},
26
26
  ): Promise<void> {
27
- const dist = opts.distDir ?? "dist";
27
+ const distDir = opts.distDir ?? "dist";
28
28
  const root = process.cwd();
29
+ const name = basename(root);
29
30
 
30
- // Get current directory name for worker name
31
- const parts = root.split("/");
32
- const name = parts[parts.length - 1];
31
+ await writeWranglerConfig(root, distDir, name);
32
+ await writeWorkerEntryPoint(root, distDir);
33
+ }
33
34
 
35
+ /**
36
+ * Write the wrangler.jsonc configuration file for Cloudflare Workers
37
+ */
38
+ async function writeWranglerConfig(
39
+ root: string,
40
+ distDir: string,
41
+ name: string,
42
+ ): Promise<void> {
34
43
  const wrangler = {
35
- name: name,
36
- main: "./worker.js",
44
+ name,
45
+ main: "./main.cloudflare.js",
37
46
  compatibility_flags: ["nodejs_compat"],
38
47
  compatibility_date: "2025-11-17",
39
48
  };
40
49
 
41
- writeFileSync(
42
- join(root, dist, "wrangler.jsonc"),
50
+ await writeFile(
51
+ join(root, distDir, "wrangler.jsonc"),
43
52
  JSON.stringify(wrangler, null, 2),
44
53
  );
54
+ }
45
55
 
56
+ /**
57
+ * Write the worker entry point that bootstraps Alepha and handles fetch requests
58
+ */
59
+ async function writeWorkerEntryPoint(
60
+ root: string,
61
+ distDir: string,
62
+ ): Promise<void> {
46
63
  const workerCode = `
47
- process.env.ALEPHA_SERVERLESS = "true";
48
-
49
- await import("./index.js");
64
+ import "./index.js";
50
65
 
51
66
  export default {
52
- fetch: async (request, env, ctx) => {
67
+ fetch: async (request) => {
68
+ const ctx = { req: request, res: undefined };
53
69
 
54
70
  await __alepha.start();
55
- const ev = { req: request, res: undefined };
56
- await __alepha.events.emit("web:request", ev);
57
- return ev.res;
71
+ await __alepha.events.emit("web:request", ctx);
72
+
73
+ return ctx.res;
58
74
  },
59
75
  };
60
76
  `.trim();
61
77
 
62
- writeFileSync(
63
- join(root, dist, "worker.js"),
78
+ await writeFile(
79
+ join(root, distDir, "main.cloudflare.js"),
64
80
  `${WARNING_COMMENT}\n${workerCode}`.trim(),
65
81
  );
66
82
  }
@@ -38,14 +38,28 @@ export async function generateDocker(
38
38
  const image = opts.image ?? "node:24-alpine";
39
39
  const command = opts.command ?? "node";
40
40
 
41
- // Copy drizzle migrations if they exist
41
+ await copyDrizzleMigrations(distDir);
42
+ await writeDockerfile(distDir, image, command);
43
+ }
44
+
45
+ /**
46
+ * Copy drizzle migrations to the dist directory if they exist
47
+ */
48
+ async function copyDrizzleMigrations(distDir: string): Promise<void> {
42
49
  const hasMigrations = await fileExists("drizzle");
43
50
  if (hasMigrations) {
44
- await cp("drizzle", `${distDir}/drizzle`, {
45
- recursive: true,
46
- });
51
+ await cp("drizzle", `${distDir}/drizzle`, { recursive: true });
47
52
  }
53
+ }
48
54
 
55
+ /**
56
+ * Write the Dockerfile with the specified base image and command
57
+ */
58
+ async function writeDockerfile(
59
+ distDir: string,
60
+ image: string,
61
+ command: string,
62
+ ): Promise<void> {
49
63
  const dockerfile = `# This file was automatically generated. DO NOT MODIFY.
50
64
  # Changes to this file will be lost when the code is regenerated.
51
65
  FROM ${image}
@@ -28,13 +28,11 @@ export interface GenerateSitemapOptions {
28
28
  export async function generateSitemap(
29
29
  opts: GenerateSitemapOptions,
30
30
  ): Promise<string> {
31
- const alepha = await importAlepha(opts.entry, {
32
- env: opts.template
33
- ? {
34
- REACT_SERVER_TEMPLATE: opts.template,
35
- }
36
- : {},
37
- });
31
+ const alepha = await importAlepha(opts.entry);
32
+
33
+ if (opts.template) {
34
+ alepha.set("alepha.react.server.template", opts.template);
35
+ }
38
36
 
39
37
  if (!alepha.isConfigured()) {
40
38
  await alepha.events.emit("configure", alepha);
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
1
+ import { mkdir, stat, writeFile } from "node:fs/promises";
2
2
  import { importVite } from "../helpers/importVite.ts";
3
3
 
4
4
  export interface GenerateVercelOptions {
@@ -52,33 +52,62 @@ export async function generateVercel(
52
52
  const distDir = opts.distDir ?? "dist";
53
53
  const clientDir = opts.clientDir ?? "public";
54
54
  const { loadEnv } = await importVite();
55
-
56
55
  const env = loadEnv("production", process.cwd(), "");
57
56
 
58
- // Ensure the api directory exists
59
- if (!existsSync(`${distDir}/api`)) {
60
- mkdirSync(`${distDir}/api`);
57
+ await writeApiEntryPoint(distDir);
58
+ await writeVercelConfig(distDir, clientDir, opts.config?.config);
59
+
60
+ const projectId = env.VERCEL_PROJECT_ID ?? opts.config?.projectId;
61
+ const projectName = env.VERCEL_PROJECT_NAME ?? opts.config?.projectName;
62
+ const orgId = env.VERCEL_ORG_ID ?? opts.config?.orgId;
63
+
64
+ if (projectId && orgId) {
65
+ await writeProjectConfig(distDir, projectId, projectName, orgId);
61
66
  }
62
67
 
63
- // Add the only one entry point for Vercel
64
- writeFileSync(
68
+ await ensureClientDir(distDir, clientDir);
69
+ }
70
+
71
+ /**
72
+ * Check if a file or directory exists at the given path
73
+ */
74
+ async function exists(path: string): Promise<boolean> {
75
+ return stat(path)
76
+ .then(() => true)
77
+ .catch(() => false);
78
+ }
79
+
80
+ /**
81
+ * Create the serverless function entry point that bootstraps Alepha and handles requests
82
+ */
83
+ async function writeApiEntryPoint(distDir: string): Promise<void> {
84
+ await mkdir(`${distDir}/api`, { recursive: true });
85
+ await writeFile(
65
86
  `${distDir}/api/index.js`,
66
87
  `${WARNING_COMMENT}
67
88
  import "../index.js";
68
89
 
69
- export default async function (req, res) {
90
+ export default async (req, res) => {
70
91
  \tawait __alepha.start();
71
- \t__alepha.events.emit("node:request", { req, res });
92
+ \tawait __alepha.events.emit("node:request", { req, res });
72
93
  }
73
94
  `,
74
95
  );
96
+ }
75
97
 
76
- // Always generate a vercel.json file
77
- writeFileSync(
98
+ /**
99
+ * Generate vercel.json with route rewrites to direct all traffic to the serverless function
100
+ */
101
+ async function writeVercelConfig(
102
+ distDir: string,
103
+ clientDir: string,
104
+ config?: VercelConfig["config"],
105
+ ): Promise<void> {
106
+ await writeFile(
78
107
  `${distDir}/vercel.json`,
79
108
  JSON.stringify(
80
109
  {
81
- ...opts?.config?.config,
110
+ ...config,
82
111
  rewrites: [
83
112
  {
84
113
  source: "/(.*)",
@@ -93,36 +122,42 @@ export default async function (req, res) {
93
122
  " ",
94
123
  ),
95
124
  );
125
+ }
96
126
 
97
- // Generate .vercel/project.json if VERCEL_PROJECT_ID and VERCEL_ORG_ID are set
98
- const projectId = env.VERCEL_PROJECT_ID ?? opts.config?.projectId;
99
- const projectName = env.VERCEL_PROJECT_NAME ?? opts.config?.projectName;
100
- const orgId = env.VERCEL_ORG_ID ?? opts.config?.orgId;
101
-
102
- if (projectId && orgId) {
103
- try {
104
- mkdirSync(`${distDir}/.vercel`, { recursive: true });
105
- } catch (_e) {
106
- // Ignore error if directory already exists
107
- }
108
-
109
- writeFileSync(
110
- `${distDir}/.vercel/project.json`,
111
- JSON.stringify(
112
- {
113
- projectId,
114
- projectName,
115
- orgId,
116
- },
117
- null,
118
- " ",
119
- ),
120
- );
121
- }
127
+ /**
128
+ * Generate .vercel/project.json to link the deployment to a Vercel project
129
+ */
130
+ async function writeProjectConfig(
131
+ distDir: string,
132
+ projectId: string,
133
+ projectName: string | undefined,
134
+ orgId: string,
135
+ ): Promise<void> {
136
+ await mkdir(`${distDir}/.vercel`, { recursive: true });
137
+ await writeFile(
138
+ `${distDir}/.vercel/project.json`,
139
+ JSON.stringify(
140
+ {
141
+ projectId,
142
+ projectName,
143
+ orgId,
144
+ },
145
+ null,
146
+ " ",
147
+ ),
148
+ );
149
+ }
122
150
 
123
- // If /public does not exist, create an empty one to avoid Vercel errors
124
- if (!existsSync(`${distDir}/${clientDir}`)) {
125
- mkdirSync(`${distDir}/${clientDir}`, { recursive: true });
126
- writeFileSync(`${distDir}/${clientDir}/.keep`, "");
151
+ /**
152
+ * Create the client directory with a .keep file if it doesn't exist to avoid Vercel errors
153
+ */
154
+ async function ensureClientDir(
155
+ distDir: string,
156
+ clientDir: string,
157
+ ): Promise<void> {
158
+ const path = `${distDir}/${clientDir}`;
159
+ if (!(await exists(path))) {
160
+ await mkdir(path, { recursive: true });
161
+ await writeFile(`${path}/.keep`, "");
127
162
  }
128
163
  }
@@ -91,6 +91,18 @@ export class AlephaRunner {
91
91
  async start(server: ViteDevServer): Promise<void> {
92
92
  const { loadEnv } = await importVite();
93
93
 
94
+ // unfortunately, vite SSR loader does not fix stack traces automatically
95
+ // for now, we make a global helper and alepha/logger will use it before logging errors
96
+ // that's not ideal but works for now
97
+ (global as any).ssrFixStacktrace = (e: Error) => {
98
+ server.ssrFixStacktrace(e);
99
+ let it: any = e;
100
+ do {
101
+ server.ssrFixStacktrace(it);
102
+ it = it.cause;
103
+ } while (it instanceof Error);
104
+ };
105
+
94
106
  if (this.state.started) {
95
107
  await this.restart(server, true);
96
108
  return;
@@ -130,7 +142,9 @@ export class AlephaRunner {
130
142
 
131
143
  try {
132
144
  const now = Date.now();
133
- await server.ssrLoadModule(fileUrl);
145
+ await server.ssrLoadModule(fileUrl, {
146
+ fixStacktrace: true,
147
+ });
134
148
  this.state.log(`[DEBUG] Alepha app loaded in ${Date.now() - now}ms`);
135
149
  await new Promise((r) => setTimeout(r, 10));
136
150
 
@@ -160,6 +174,7 @@ export class AlephaRunner {
160
174
  if (e.cause instanceof Error) {
161
175
  server.ssrFixStacktrace(e.cause);
162
176
  }
177
+
163
178
  this.state.app?.log?.error("App failed to start:", e);
164
179
  this.state.app?.log?.info("Waiting for changes to restart...");
165
180
  }
@@ -1,13 +1,5 @@
1
1
  import type { IncomingMessage } from "node:http";
2
- import {
3
- $env,
4
- $hook,
5
- $inject,
6
- Alepha,
7
- type Static,
8
- TypeBoxValue,
9
- t,
10
- } from "alepha";
2
+ import { $env, $hook, $inject, Alepha, type Static, t, Value } from "alepha";
11
3
  import { $logger } from "alepha/logger";
12
4
  import { WebSocket, WebSocketServer } from "ws";
13
5
  import { WebSocketValidationError } from "../errors/WebSocketError.ts";
@@ -507,8 +499,8 @@ export class NodeWebSocketConnection implements WebSocketConnection {
507
499
 
508
500
  // Validate message against schema (out = client→server)
509
501
  const outSchema = this.endpoint.channel.options.schema.out;
510
- if (!TypeBoxValue.Check(outSchema, message)) {
511
- const errors = Array.from(TypeBoxValue.Errors(outSchema, message));
502
+ if (!Value.Check(outSchema, message)) {
503
+ const errors = Array.from(Value.Errors(outSchema, message));
512
504
  throw new WebSocketValidationError(
513
505
  `Message validation failed: ${errors.map((e: any) => e.message).join(", ")}`,
514
506
  );
@@ -4,8 +4,8 @@ import {
4
4
  Alepha,
5
5
  AlephaError,
6
6
  type Static,
7
- TypeBoxValue,
8
7
  t,
8
+ Value,
9
9
  } from "alepha";
10
10
  import { $logger } from "alepha/logger";
11
11
  import type { ChannelPrimitive, TWSObject } from "../primitives/$channel.ts";
@@ -332,8 +332,8 @@ export class WebSocketChannelConnection<
332
332
 
333
333
  // Validate outgoing message against schema
334
334
  const outSchema = this.channel.options.schema.out;
335
- if (!TypeBoxValue.Check(outSchema, message)) {
336
- const errors = Array.from(TypeBoxValue.Errors(outSchema, message));
335
+ if (!Value.Check(outSchema, message)) {
336
+ const errors = Array.from(Value.Errors(outSchema, message));
337
337
  this.log.warn("Message validation failed", { errors });
338
338
  throw new Error(
339
339
  `Message validation failed: ${errors.map((e) => e.message).join(", ")}`,