alepha 0.15.0 → 0.15.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.
Files changed (222) hide show
  1. package/README.md +43 -98
  2. package/dist/api/audits/index.d.ts +240 -240
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/audits/index.js +2 -2
  5. package/dist/api/audits/index.js.map +1 -1
  6. package/dist/api/files/index.d.ts +185 -185
  7. package/dist/api/files/index.d.ts.map +1 -1
  8. package/dist/api/files/index.js +2 -2
  9. package/dist/api/files/index.js.map +1 -1
  10. package/dist/api/jobs/index.d.ts +245 -245
  11. package/dist/api/jobs/index.d.ts.map +1 -1
  12. package/dist/api/notifications/index.browser.js +4 -4
  13. package/dist/api/notifications/index.browser.js.map +1 -1
  14. package/dist/api/notifications/index.d.ts +74 -74
  15. package/dist/api/notifications/index.d.ts.map +1 -1
  16. package/dist/api/notifications/index.js +4 -4
  17. package/dist/api/notifications/index.js.map +1 -1
  18. package/dist/api/parameters/index.d.ts +221 -221
  19. package/dist/api/parameters/index.d.ts.map +1 -1
  20. package/dist/api/users/index.d.ts +1632 -1631
  21. package/dist/api/users/index.d.ts.map +1 -1
  22. package/dist/api/users/index.js +26 -34
  23. package/dist/api/users/index.js.map +1 -1
  24. package/dist/api/verifications/index.d.ts +132 -132
  25. package/dist/api/verifications/index.d.ts.map +1 -1
  26. package/dist/batch/index.d.ts +122 -122
  27. package/dist/batch/index.d.ts.map +1 -1
  28. package/dist/bucket/index.d.ts +163 -163
  29. package/dist/bucket/index.d.ts.map +1 -1
  30. package/dist/cache/core/index.d.ts +46 -46
  31. package/dist/cache/core/index.d.ts.map +1 -1
  32. package/dist/cache/redis/index.d.ts.map +1 -1
  33. package/dist/cache/redis/index.js +2 -2
  34. package/dist/cache/redis/index.js.map +1 -1
  35. package/dist/cli/index.d.ts +5933 -201
  36. package/dist/cli/index.d.ts.map +1 -1
  37. package/dist/cli/index.js +609 -169
  38. package/dist/cli/index.js.map +1 -1
  39. package/dist/command/index.d.ts +296 -296
  40. package/dist/command/index.d.ts.map +1 -1
  41. package/dist/command/index.js +19 -19
  42. package/dist/command/index.js.map +1 -1
  43. package/dist/core/index.browser.js +268 -79
  44. package/dist/core/index.browser.js.map +1 -1
  45. package/dist/core/index.d.ts +768 -694
  46. package/dist/core/index.d.ts.map +1 -1
  47. package/dist/core/index.js +268 -79
  48. package/dist/core/index.js.map +1 -1
  49. package/dist/core/index.native.js +268 -79
  50. package/dist/core/index.native.js.map +1 -1
  51. package/dist/datetime/index.d.ts +44 -44
  52. package/dist/datetime/index.d.ts.map +1 -1
  53. package/dist/email/index.d.ts +25 -25
  54. package/dist/email/index.d.ts.map +1 -1
  55. package/dist/fake/index.d.ts +5409 -5409
  56. package/dist/fake/index.d.ts.map +1 -1
  57. package/dist/fake/index.js +22 -22
  58. package/dist/fake/index.js.map +1 -1
  59. package/dist/file/index.d.ts +435 -435
  60. package/dist/file/index.d.ts.map +1 -1
  61. package/dist/lock/core/index.d.ts +208 -208
  62. package/dist/lock/core/index.d.ts.map +1 -1
  63. package/dist/lock/redis/index.d.ts.map +1 -1
  64. package/dist/logger/index.d.ts +24 -24
  65. package/dist/logger/index.d.ts.map +1 -1
  66. package/dist/logger/index.js +1 -5
  67. package/dist/logger/index.js.map +1 -1
  68. package/dist/mcp/index.d.ts +216 -198
  69. package/dist/mcp/index.d.ts.map +1 -1
  70. package/dist/mcp/index.js +28 -4
  71. package/dist/mcp/index.js.map +1 -1
  72. package/dist/orm/index.browser.js +9 -9
  73. package/dist/orm/index.browser.js.map +1 -1
  74. package/dist/orm/index.bun.js +83 -76
  75. package/dist/orm/index.bun.js.map +1 -1
  76. package/dist/orm/index.d.ts +961 -960
  77. package/dist/orm/index.d.ts.map +1 -1
  78. package/dist/orm/index.js +88 -81
  79. package/dist/orm/index.js.map +1 -1
  80. package/dist/queue/core/index.d.ts +244 -244
  81. package/dist/queue/core/index.d.ts.map +1 -1
  82. package/dist/queue/redis/index.d.ts.map +1 -1
  83. package/dist/redis/index.d.ts +105 -105
  84. package/dist/redis/index.d.ts.map +1 -1
  85. package/dist/retry/index.d.ts +69 -69
  86. package/dist/retry/index.d.ts.map +1 -1
  87. package/dist/router/index.d.ts +6 -6
  88. package/dist/router/index.d.ts.map +1 -1
  89. package/dist/scheduler/index.d.ts +108 -26
  90. package/dist/scheduler/index.d.ts.map +1 -1
  91. package/dist/scheduler/index.js +393 -1
  92. package/dist/scheduler/index.js.map +1 -1
  93. package/dist/security/index.d.ts +532 -209
  94. package/dist/security/index.d.ts.map +1 -1
  95. package/dist/security/index.js +1422 -11
  96. package/dist/security/index.js.map +1 -1
  97. package/dist/server/auth/index.d.ts +1296 -271
  98. package/dist/server/auth/index.d.ts.map +1 -1
  99. package/dist/server/auth/index.js +1249 -18
  100. package/dist/server/auth/index.js.map +1 -1
  101. package/dist/server/cache/index.d.ts +56 -56
  102. package/dist/server/cache/index.d.ts.map +1 -1
  103. package/dist/server/compress/index.d.ts +3 -3
  104. package/dist/server/compress/index.d.ts.map +1 -1
  105. package/dist/server/cookies/index.d.ts +6 -6
  106. package/dist/server/cookies/index.d.ts.map +1 -1
  107. package/dist/server/core/index.d.ts +196 -186
  108. package/dist/server/core/index.d.ts.map +1 -1
  109. package/dist/server/core/index.js +43 -27
  110. package/dist/server/core/index.js.map +1 -1
  111. package/dist/server/cors/index.d.ts +11 -11
  112. package/dist/server/cors/index.d.ts.map +1 -1
  113. package/dist/server/health/index.d.ts.map +1 -1
  114. package/dist/server/helmet/index.d.ts +2 -2
  115. package/dist/server/helmet/index.d.ts.map +1 -1
  116. package/dist/server/links/index.browser.js +9 -1
  117. package/dist/server/links/index.browser.js.map +1 -1
  118. package/dist/server/links/index.d.ts +83 -83
  119. package/dist/server/links/index.d.ts.map +1 -1
  120. package/dist/server/links/index.js +13 -5
  121. package/dist/server/links/index.js.map +1 -1
  122. package/dist/server/metrics/index.d.ts +514 -1
  123. package/dist/server/metrics/index.d.ts.map +1 -1
  124. package/dist/server/metrics/index.js +4462 -4
  125. package/dist/server/metrics/index.js.map +1 -1
  126. package/dist/server/multipart/index.d.ts +6 -6
  127. package/dist/server/multipart/index.d.ts.map +1 -1
  128. package/dist/server/proxy/index.d.ts +102 -102
  129. package/dist/server/proxy/index.d.ts.map +1 -1
  130. package/dist/server/rate-limit/index.d.ts +16 -16
  131. package/dist/server/rate-limit/index.d.ts.map +1 -1
  132. package/dist/server/static/index.d.ts +44 -44
  133. package/dist/server/static/index.d.ts.map +1 -1
  134. package/dist/server/swagger/index.d.ts +47 -47
  135. package/dist/server/swagger/index.d.ts.map +1 -1
  136. package/dist/sms/index.d.ts +11 -11
  137. package/dist/sms/index.d.ts.map +1 -1
  138. package/dist/sms/index.js +3 -3
  139. package/dist/sms/index.js.map +1 -1
  140. package/dist/thread/index.d.ts +71 -71
  141. package/dist/thread/index.d.ts.map +1 -1
  142. package/dist/thread/index.js +2 -2
  143. package/dist/thread/index.js.map +1 -1
  144. package/dist/topic/core/index.d.ts +318 -318
  145. package/dist/topic/core/index.d.ts.map +1 -1
  146. package/dist/topic/redis/index.d.ts +6 -6
  147. package/dist/topic/redis/index.d.ts.map +1 -1
  148. package/dist/vite/index.d.ts +2324 -1719
  149. package/dist/vite/index.d.ts.map +1 -1
  150. package/dist/vite/index.js +123 -475
  151. package/dist/vite/index.js.map +1 -1
  152. package/dist/websocket/index.browser.js +3 -3
  153. package/dist/websocket/index.browser.js.map +1 -1
  154. package/dist/websocket/index.d.ts +275 -275
  155. package/dist/websocket/index.d.ts.map +1 -1
  156. package/dist/websocket/index.js +3 -3
  157. package/dist/websocket/index.js.map +1 -1
  158. package/package.json +9 -9
  159. package/src/api/users/services/SessionService.ts +0 -10
  160. package/src/cli/apps/AlephaCli.ts +2 -2
  161. package/src/cli/apps/AlephaPackageBuilderCli.ts +9 -1
  162. package/src/cli/assets/apiHelloControllerTs.ts +2 -1
  163. package/src/cli/assets/biomeJson.ts +2 -1
  164. package/src/cli/assets/claudeMd.ts +9 -4
  165. package/src/cli/assets/dummySpecTs.ts +2 -1
  166. package/src/cli/assets/editorconfig.ts +2 -1
  167. package/src/cli/assets/mainBrowserTs.ts +2 -1
  168. package/src/cli/assets/mainCss.ts +24 -0
  169. package/src/cli/assets/tsconfigJson.ts +2 -1
  170. package/src/cli/assets/webAppRouterTs.ts +2 -1
  171. package/src/cli/assets/webHelloComponentTsx.ts +6 -2
  172. package/src/cli/atoms/appEntryOptions.ts +13 -0
  173. package/src/cli/atoms/buildOptions.ts +1 -1
  174. package/src/cli/atoms/changelogOptions.ts +1 -1
  175. package/src/cli/commands/build.ts +63 -47
  176. package/src/cli/commands/dev.ts +16 -33
  177. package/src/cli/commands/gen/env.ts +1 -1
  178. package/src/cli/commands/init.ts +17 -8
  179. package/src/cli/commands/lint.ts +1 -1
  180. package/src/cli/defineConfig.ts +9 -0
  181. package/src/cli/index.ts +2 -1
  182. package/src/cli/providers/AppEntryProvider.ts +131 -0
  183. package/src/cli/providers/ViteBuildProvider.ts +82 -0
  184. package/src/cli/providers/ViteDevServerProvider.ts +350 -0
  185. package/src/cli/providers/ViteTemplateProvider.ts +27 -0
  186. package/src/cli/services/AlephaCliUtils.ts +33 -2
  187. package/src/cli/services/PackageManagerUtils.ts +13 -6
  188. package/src/cli/services/ProjectScaffolder.ts +72 -49
  189. package/src/core/Alepha.ts +2 -8
  190. package/src/core/primitives/$module.ts +12 -0
  191. package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +257 -0
  192. package/src/core/providers/KeylessJsonSchemaCodec.ts +396 -14
  193. package/src/core/providers/SchemaValidator.spec.ts +236 -0
  194. package/src/logger/providers/PrettyFormatterProvider.ts +0 -9
  195. package/src/mcp/errors/McpError.ts +30 -0
  196. package/src/mcp/index.ts +3 -0
  197. package/src/mcp/transports/SseMcpTransport.ts +16 -6
  198. package/src/orm/providers/DrizzleKitProvider.ts +3 -5
  199. package/src/orm/services/Repository.ts +11 -0
  200. package/src/server/core/index.ts +1 -1
  201. package/src/server/core/providers/BunHttpServerProvider.ts +1 -1
  202. package/src/server/core/providers/NodeHttpServerProvider.spec.ts +125 -0
  203. package/src/server/core/providers/NodeHttpServerProvider.ts +71 -22
  204. package/src/server/core/providers/ServerLoggerProvider.ts +2 -2
  205. package/src/server/core/providers/ServerProvider.ts +9 -12
  206. package/src/server/links/atoms/apiLinksAtom.ts +7 -0
  207. package/src/server/links/index.browser.ts +2 -0
  208. package/src/server/links/index.ts +2 -0
  209. package/src/vite/index.ts +3 -2
  210. package/src/vite/tasks/buildClient.ts +0 -1
  211. package/src/vite/tasks/buildServer.ts +68 -21
  212. package/src/vite/tasks/copyAssets.ts +5 -4
  213. package/src/vite/tasks/generateSitemap.ts +64 -23
  214. package/src/vite/tasks/index.ts +0 -2
  215. package/src/vite/tasks/prerenderPages.ts +49 -24
  216. package/src/cli/assets/indexHtml.ts +0 -15
  217. package/src/cli/commands/format.ts +0 -23
  218. package/src/vite/helpers/boot.ts +0 -117
  219. package/src/vite/plugins/viteAlephaDev.ts +0 -177
  220. package/src/vite/tasks/devServer.ts +0 -71
  221. package/src/vite/tasks/runAlepha.ts +0 -270
  222. /package/dist/orm/{chunk-DtkW-qnP.js → chunk-DH6iiROE.js} +0 -0
@@ -1,6 +1,6 @@
1
1
  import { readFile, rm, writeFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
- import { AlephaError } from "alepha";
3
+ import { type Alepha, AlephaError } from "alepha";
4
4
  import type * as vite from "vite";
5
5
  import type { UserConfig } from "vite";
6
6
  import { analyzer as viteAnalyzer } from "vite-bundle-analyzer";
@@ -27,11 +27,6 @@ export interface BuildServerOptions {
27
27
  */
28
28
  clientDir?: string;
29
29
 
30
- /**
31
- * Override Vite config options.
32
- */
33
- config?: UserConfig;
34
-
35
30
  /**
36
31
  * If true, generate build stats report.
37
32
  */
@@ -48,6 +43,11 @@ export interface BuildServerOptions {
48
43
  * Add more entry point conditions for SSR module resolution (e.g., "bun").
49
44
  */
50
45
  conditions?: string[];
46
+
47
+ /**
48
+ * Alepha instance to set SSR manifest for pre-rendering.
49
+ */
50
+ alepha: Alepha;
51
51
  }
52
52
 
53
53
  export interface BuildServerResult {
@@ -55,6 +55,15 @@ export interface BuildServerResult {
55
55
  * The filename of the built server entry (e.g., "abc123.js").
56
56
  */
57
57
  entryFile: string;
58
+
59
+ /**
60
+ * SSR manifest data for module preloading.
61
+ * Can be used to set in alepha store for pre-rendering.
62
+ */
63
+ manifest?: {
64
+ client?: Record<string, any>;
65
+ preload?: Record<string, string>;
66
+ };
58
67
  }
59
68
 
60
69
  /**
@@ -67,7 +76,7 @@ export interface BuildServerResult {
67
76
  export async function buildServer(
68
77
  opts: BuildServerOptions,
69
78
  ): Promise<BuildServerResult> {
70
- const { build: viteBuild, mergeConfig } = await importVite();
79
+ const { build: viteBuild, resolveConfig } = await importVite();
71
80
  const plugins: any[] = [];
72
81
 
73
82
  const viteReact = await importViteReact();
@@ -93,6 +102,7 @@ export async function buildServer(
93
102
  conditions.unshift(...opts.conditions);
94
103
  }
95
104
 
105
+ // note: we still can override this config via config file (vite.config.ts)
96
106
  const viteBuildServerConfig: UserConfig = {
97
107
  mode: "production",
98
108
  logLevel: opts.silent ? "silent" : undefined,
@@ -121,24 +131,32 @@ export async function buildServer(
121
131
  },
122
132
  },
123
133
  esbuild: { legalComments: "none", keepNames: true },
124
- customLogger: logger,
134
+ customLogger: logger, // mock logger to avoid noisy output
125
135
  plugins,
126
136
  };
127
137
 
138
+ // create inline config by merging alepha built-in + extended config
139
+
128
140
  let result: vite.Rollup.RollupOutput | vite.Rollup.RollupOutput[];
129
141
  try {
130
- result = (await viteBuild(
131
- mergeConfig(viteBuildServerConfig, opts.config || {}),
132
- )) as vite.Rollup.RollupOutput | vite.Rollup.RollupOutput[];
142
+ result = (await viteBuild(viteBuildServerConfig)) as
143
+ | vite.Rollup.RollupOutput
144
+ | vite.Rollup.RollupOutput[];
133
145
  } catch (error) {
134
- // Flush buffered logs on failure so user can see what happened
146
+ // flush buffered logs on failure so user can see what happened
135
147
  logger?.flush();
136
148
  throw error;
137
149
  }
138
150
 
139
- // Extract resolved config to get externals
140
- const resolvedConfig = (result as any).resolvedConfig;
141
- const externals: string[] = resolvedConfig?.ssr?.external ?? [];
151
+ // resolve final config to read externals
152
+ const resolvedConfig = await resolveConfig(viteBuildServerConfig, "build");
153
+
154
+ // extract resolved config to get externals
155
+ const externals: string[] = [];
156
+
157
+ if (Array.isArray(resolvedConfig?.ssr?.external)) {
158
+ externals.push(...resolvedConfig.ssr.external);
159
+ }
142
160
 
143
161
  // Generate package.json with externals
144
162
  await generateExternals({
@@ -161,21 +179,29 @@ export async function buildServer(
161
179
  // Embed SSR manifests if client was built
162
180
  // This bundles all manifest data into index.js for serverless deployments
163
181
  let manifest = "";
182
+ let manifestData:
183
+ | { client?: Record<string, any>; preload?: Record<string, string> }
184
+ | undefined;
185
+
164
186
  if (opts.clientDir) {
165
187
  const viteDir = `${opts.distDir}/${opts.clientDir}/.vite`;
166
- const ssrManifest = await loadJsonFile(`${viteDir}/ssr-manifest.json`);
167
188
  const clientManifest = await loadJsonFile(`${viteDir}/manifest.json`);
168
189
  const preloadManifest = await loadJsonFile(
169
190
  `${viteDir}/preload-manifest.json`,
170
191
  );
171
192
 
172
- const combined = {
173
- ssr: ssrManifest,
174
- client: clientManifest,
193
+ // Strip unused fields from client manifest to reduce bundle size
194
+ const strippedClientManifest = stripClientManifest(clientManifest);
195
+
196
+ manifestData = {
197
+ client: strippedClientManifest,
175
198
  preload: preloadManifest,
176
199
  };
177
200
 
178
- manifest = `__alepha.set("alepha.react.ssr.manifest", ${JSON.stringify(combined)});\n`;
201
+ manifest = `__alepha.set("alepha.react.ssr.manifest", ${JSON.stringify(manifestData)});\n`;
202
+
203
+ // Set manifest in alepha store for pre-rendering
204
+ opts.alepha.store.set("alepha.react.ssr.manifest" as any, manifestData);
179
205
 
180
206
  // Remove .vite directory - no longer needed at runtime
181
207
  await rm(viteDir, { recursive: true, force: true });
@@ -191,7 +217,7 @@ export async function buildServer(
191
217
  `${warning}\n${template}${manifest}import './server/${entryFile}';\n`.trim(),
192
218
  );
193
219
 
194
- return { entryFile };
220
+ return { entryFile, manifest: manifestData };
195
221
  }
196
222
 
197
223
  /**
@@ -206,6 +232,27 @@ async function loadJsonFile(path: string): Promise<any> {
206
232
  }
207
233
  }
208
234
 
235
+ /**
236
+ * Strip unused fields from client manifest to reduce bundle size.
237
+ * Only keeps: file, isEntry, imports, css
238
+ */
239
+ function stripClientManifest(
240
+ manifest: Record<string, any> | undefined,
241
+ ): Record<string, any> | undefined {
242
+ if (!manifest) return undefined;
243
+
244
+ const stripped: Record<string, any> = {};
245
+ for (const [key, entry] of Object.entries(manifest)) {
246
+ stripped[key] = {
247
+ file: entry.file,
248
+ ...(entry.isEntry && { isEntry: entry.isEntry }),
249
+ ...(entry.imports?.length && { imports: entry.imports }),
250
+ ...(entry.css?.length && { css: entry.css }),
251
+ };
252
+ }
253
+ return stripped;
254
+ }
255
+
209
256
  /**
210
257
  * Extract entry filename from Vite build result.
211
258
  */
@@ -1,9 +1,10 @@
1
1
  import { cp, mkdir } from "node:fs/promises";
2
2
  import { createRequire } from "node:module";
3
3
  import { dirname, join, resolve } from "node:path";
4
- import { importAlepha } from "../helpers/importAlepha.ts";
4
+ import type { Alepha } from "alepha";
5
5
 
6
6
  export interface CopyAssetsOptions {
7
+ alepha: Alepha;
7
8
  /**
8
9
  * Entry point for the built Alepha application.
9
10
  */
@@ -17,7 +18,7 @@ export interface CopyAssetsOptions {
17
18
  /**
18
19
  * @default process.cwd()
19
20
  */
20
- root?: string;
21
+ root: string;
21
22
 
22
23
  /**
23
24
  * Add Runner for logging (@see Alepha CLI)
@@ -38,8 +39,8 @@ export interface CopyAssetsOptions {
38
39
  * Used by modules like AlephaServerSwagger to distribute UI files.
39
40
  */
40
41
  export async function copyAssets(opts: CopyAssetsOptions): Promise<void> {
41
- const root = opts.root ?? process.cwd();
42
- const alepha = await importAlepha(opts.entry);
42
+ const root = opts.root;
43
+ const alepha = opts.alepha;
43
44
  const assets = alepha.store.get("alepha.build.assets");
44
45
 
45
46
  if (!assets || assets.length === 0) {
@@ -1,11 +1,11 @@
1
+ import { writeFile } from "node:fs/promises";
1
2
  import type { Alepha } from "alepha";
2
- import { importAlepha } from "../helpers/importAlepha.ts";
3
3
 
4
4
  export interface GenerateSitemapOptions {
5
5
  /**
6
- * Entry point for the built Alepha application.
6
+ * Alepha instance to use for generating the sitemap.
7
7
  */
8
- entry: string;
8
+ alepha: Alepha;
9
9
 
10
10
  /**
11
11
  * Base URL for the sitemap (e.g., "https://example.com").
@@ -13,52 +13,93 @@ export interface GenerateSitemapOptions {
13
13
  baseUrl: string;
14
14
 
15
15
  /**
16
- * Optional HTML template (for React SSR).
16
+ * Output file path for the sitemap. If provided, writes the sitemap to disk.
17
17
  */
18
- template?: string;
18
+ output?: string;
19
+
20
+ /**
21
+ * Add Runner for logging (@see Alepha CLI)
22
+ */
23
+ run?: (opts: {
24
+ name: string;
25
+ handler: () => Promise<void>;
26
+ }) => Promise<string>;
19
27
  }
20
28
 
21
29
  /**
22
30
  * Generate sitemap.xml from Alepha page primitives.
23
31
  *
24
- * This task loads the built Alepha application,
25
- * queries all page primitives, and generates a sitemap.xml
32
+ * Queries all page primitives and generates a sitemap.xml
26
33
  * containing URLs for all accessible pages.
27
34
  */
28
35
  export async function generateSitemap(
29
36
  opts: GenerateSitemapOptions,
30
37
  ): Promise<string> {
31
- const alepha = await importAlepha(opts.entry);
38
+ const pages = getSitemapPages(opts.alepha);
32
39
 
33
- if (opts.template) {
34
- alepha.set("alepha.react.server.template", opts.template);
40
+ // Nothing to generate
41
+ if (pages.length === 0) {
42
+ return "";
35
43
  }
36
44
 
37
- if (!alepha.isConfigured()) {
38
- await alepha.events.emit("configure", alepha);
39
- (alepha as any).configured = true;
45
+ let result = "";
46
+
47
+ const fn = async () => {
48
+ result = generateSitemapFromPages(pages, opts.baseUrl);
49
+ if (opts.output) {
50
+ await writeFile(opts.output, result);
51
+ }
52
+ };
53
+
54
+ if (opts.run) {
55
+ await opts.run({
56
+ name: "generate sitemap",
57
+ handler: fn,
58
+ });
59
+ } else {
60
+ await fn();
40
61
  }
41
62
 
42
- return generateSitemapFromAlepha(alepha, opts.baseUrl);
63
+ return result;
43
64
  }
44
65
 
45
- function generateSitemapFromAlepha(alepha: Alepha, baseUrl: string): string {
66
+ /**
67
+ * Get all pages that should be included in the sitemap.
68
+ */
69
+ function getSitemapPages(alepha: Alepha): any[] {
46
70
  const pages = alepha.primitives("page") as any[];
47
- const urls: string[] = [];
48
-
49
- for (const page of pages) {
71
+ return pages.filter((page) => {
50
72
  const options = page.options;
51
-
52
73
  // Skip pages with children (parent pages that can't be rendered directly)
53
74
  if (options.children) {
54
- continue;
75
+ return false;
76
+ }
77
+ // Include pages without parameters or static pages with entries
78
+ if (!options.schema?.params) {
79
+ return true;
80
+ }
81
+ if (
82
+ options.static &&
83
+ typeof options.static === "object" &&
84
+ options.static.entries
85
+ ) {
86
+ return true;
55
87
  }
88
+ return false;
89
+ });
90
+ }
91
+
92
+ function generateSitemapFromPages(pages: any[], baseUrl: string): string {
93
+ const urls: string[] = [];
94
+ const normalizedBaseUrl = baseUrl.replace(/\/$/, "");
95
+
96
+ for (const page of pages) {
97
+ const options = page.options;
56
98
 
57
- // Only include static pages or pages without parameters
58
99
  if (!options.schema?.params) {
59
100
  // Simple page without parameters
60
101
  const path = options.path || "";
61
- const url = `${baseUrl.replace(/\/$/, "")}${path === "" ? "/" : path}`;
102
+ const url = `${normalizedBaseUrl}${path === "" ? "/" : path}`;
62
103
  urls.push(url);
63
104
  } else if (
64
105
  options.static &&
@@ -71,7 +112,7 @@ function generateSitemapFromAlepha(alepha: Alepha, baseUrl: string): string {
71
112
  options.path || "",
72
113
  entry.params || {},
73
114
  );
74
- const url = `${baseUrl.replace(/\/$/, "")}${path}`;
115
+ const url = `${normalizedBaseUrl}${path}`;
75
116
  urls.push(url);
76
117
  }
77
118
  }
@@ -13,11 +13,9 @@
13
13
  export * from "./buildClient.ts";
14
14
  export * from "./buildServer.ts";
15
15
  export * from "./copyAssets.ts";
16
- export * from "./devServer.ts";
17
16
  export * from "./generateCloudflare.ts";
18
17
  export * from "./generateDocker.ts";
19
18
  export * from "./generateExternals.ts";
20
19
  export * from "./generateSitemap.ts";
21
20
  export * from "./generateVercel.ts";
22
21
  export * from "./prerenderPages.ts";
23
- export * from "./runAlepha.ts";
@@ -1,6 +1,5 @@
1
1
  import { mkdir, writeFile } from "node:fs/promises";
2
2
  import type { Alepha } from "alepha";
3
- import { importAlepha } from "../helpers/importAlepha.ts";
4
3
  import {
5
4
  compressFile,
6
5
  type ViteCompressOptions,
@@ -8,9 +7,9 @@ import {
8
7
 
9
8
  export interface PrerenderPagesOptions {
10
9
  /**
11
- * Entry point for the built Alepha application.
10
+ * Alepha instance to use for pre-rendering.
12
11
  */
13
- entry: string;
12
+ alepha: Alepha;
14
13
 
15
14
  /**
16
15
  * Client dist directory for output files.
@@ -21,6 +20,14 @@ export interface PrerenderPagesOptions {
21
20
  * Optional compression options.
22
21
  */
23
22
  compress?: ViteCompressOptions | boolean;
23
+
24
+ /**
25
+ * Add Runner for logging (@see Alepha CLI)
26
+ */
27
+ run?: (opts: {
28
+ name: string;
29
+ handler: () => Promise<void>;
30
+ }) => Promise<string>;
24
31
  }
25
32
 
26
33
  export interface PrerenderPagesResult {
@@ -33,45 +40,63 @@ export interface PrerenderPagesResult {
33
40
  /**
34
41
  * Pre-render static pages defined in the Alepha application.
35
42
  *
36
- * This task loads the built Alepha application, queries all page
37
- * primitives with `static: true`, and generates static HTML files
38
- * for each page. Supports pages with parameterized routes via
39
- * `static.entries` configuration.
43
+ * Queries all page primitives with `static: true` and generates
44
+ * static HTML files for each page. Supports pages with parameterized
45
+ * routes via `static.entries` configuration.
40
46
  */
41
47
  export async function prerenderPages(
42
48
  opts: PrerenderPagesOptions,
43
49
  ): Promise<PrerenderPagesResult> {
44
- const alepha = await importAlepha(opts.entry);
50
+ const alepha = opts.alepha;
51
+ const pages = getStaticPages(alepha);
45
52
 
46
- const now = Date.now();
53
+ // Nothing to pre-render
54
+ if (pages.length === 0) {
55
+ return { count: 0 };
56
+ }
57
+
58
+ let result: PrerenderPagesResult = { count: 0 };
47
59
 
48
- if (!alepha.isConfigured()) {
49
- await alepha.events.emit("configure", alepha);
50
- (alepha as any).configured = true;
60
+ const fn = async () => {
61
+ // TODO: running configure here is a temporary workaround
62
+ if (!alepha.isConfigured()) {
63
+ await alepha.events.emit("configure", alepha);
64
+ }
65
+ result = await prerenderFromAlepha(pages, opts.dist, opts.compress);
66
+ };
67
+
68
+ if (opts.run) {
69
+ await opts.run({
70
+ name: "pre-render pages",
71
+ handler: fn,
72
+ });
73
+ } else {
74
+ await fn();
51
75
  }
52
76
 
53
- return await prerenderFromAlepha(alepha, opts.dist, opts.compress);
77
+ return result;
78
+ }
79
+
80
+ /**
81
+ * Get all static pages from the Alepha instance.
82
+ */
83
+ function getStaticPages(alepha: Alepha): any[] {
84
+ const pages = alepha.primitives("page") as any[];
85
+ return pages.filter((page) => {
86
+ const options = page.options;
87
+ return options.static && !options.children;
88
+ });
54
89
  }
55
90
 
56
91
  async function prerenderFromAlepha(
57
- alepha: Alepha,
92
+ pages: any[],
58
93
  dist: string,
59
94
  compress?: ViteCompressOptions | boolean,
60
95
  ): Promise<PrerenderPagesResult> {
61
96
  let count = 0;
62
- const pages = alepha.primitives("page") as any[];
63
97
 
64
98
  for (const page of pages) {
65
99
  const options = page.options;
66
-
67
- if (options.children) {
68
- continue;
69
- }
70
-
71
- if (!options.static) {
72
- continue;
73
- }
74
-
75
100
  const config = typeof options.static === "object" ? options.static : {};
76
101
 
77
102
  if (!options.schema?.params) {
@@ -1,15 +0,0 @@
1
- export const indexHtml = (
2
- browserEntry: string,
3
- ) => `
4
- <!DOCTYPE html>
5
- <html lang="en">
6
- <head>
7
- <meta charset="UTF-8">
8
- <title>App</title>
9
- </head>
10
- <body>
11
- <div id="root"></div>
12
- <script type="module" src="${browserEntry}"></script>
13
- </body>
14
- </html>
15
- `.trim();
@@ -1,23 +0,0 @@
1
- import { $inject } from "alepha";
2
- import { $command } from "alepha/command";
3
- import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
4
- import { PackageManagerUtils } from "../services/PackageManagerUtils.ts";
5
- import { ProjectScaffolder } from "../services/ProjectScaffolder.ts";
6
-
7
- export class FormatCommand {
8
- protected readonly utils = $inject(AlephaCliUtils);
9
- protected readonly pm = $inject(PackageManagerUtils);
10
- protected readonly scaffolder = $inject(ProjectScaffolder);
11
-
12
- public readonly format = $command({
13
- name: "format",
14
- description: "Format the codebase using Biome",
15
- handler: async ({ root }) => {
16
- await this.scaffolder.ensureConfig(root, { biomeJson: true });
17
- await this.pm.ensureDependency(root, "@biomejs/biome", {
18
- exec: (cmd, opts) => this.utils.exec(cmd, opts),
19
- });
20
- await this.utils.exec("biome format --fix");
21
- },
22
- });
23
- }
@@ -1,117 +0,0 @@
1
- import { access, readdir, readFile } from "node:fs/promises";
2
- import { join } from "node:path";
3
- import { AlephaError } from "alepha";
4
-
5
- /**
6
- * Remember:
7
- * At first, functions was inside alepha/vite package, but it's now used in alepha too.
8
- * For avoiding cli -> vite, all code moved here.
9
- */
10
-
11
- /**
12
- * Server entry files in priority order.
13
- * main.server.ts is preferred over main.ts for consistency.
14
- */
15
- const SERVER_ENTRIES = [
16
- "main.server.ts",
17
- "main.server.tsx",
18
- "main.ts",
19
- "main.tsx",
20
- ] as const;
21
-
22
- /**
23
- * Find browser/client entry file path.
24
- */
25
- const getClientEntry = async (
26
- root = process.cwd(),
27
- ): Promise<string | undefined> => {
28
- const indexPath = join(root, "index.html");
29
- try {
30
- const html = await readFile(indexPath, "utf8");
31
- return extractFirstModuleScriptSrc(html).replace(/\\/g, "/");
32
- } catch {
33
- return undefined;
34
- }
35
- };
36
-
37
- /**
38
- * Find server entry file path.
39
- *
40
- * Optimized to use a single readdir() call instead of multiple access() calls.
41
- */
42
- const getServerEntry = async (
43
- root = process.cwd(),
44
- explicitEntry?: string,
45
- ): Promise<string> => {
46
- if (explicitEntry) {
47
- const explicitPath = join(root, explicitEntry);
48
- try {
49
- await access(explicitPath);
50
- return explicitPath;
51
- } catch {
52
- throw new AlephaError(
53
- `Explicit server entry file "${explicitEntry}" not found.`,
54
- );
55
- }
56
- }
57
-
58
- // Single IO: read src/ directory listing
59
- const srcDir = join(root, "src");
60
- try {
61
- const files = new Set(await readdir(srcDir));
62
-
63
- // Find first matching entry in priority order
64
- for (const entry of SERVER_ENTRIES) {
65
- if (files.has(entry)) {
66
- return join(srcDir, entry).replace(/\\/g, "/");
67
- }
68
- }
69
- } catch {
70
- // src/ directory doesn't exist, fall through to client entry
71
- }
72
-
73
- // Fallback: try client entry from index.html
74
- const clientEntry = await getClientEntry(root);
75
- if (clientEntry) {
76
- return clientEntry;
77
- }
78
-
79
- const fullPaths = SERVER_ENTRIES.map((e) => `src/${e}`);
80
- throw new AlephaError(
81
- `Could not find a server entry file. Supported entries: ${fullPaths.join(", ")}`,
82
- );
83
- };
84
-
85
- /**
86
- * Extract first module script src from HTML.
87
- */
88
- function extractFirstModuleScriptSrc(html: string): string {
89
- const scriptRegex = /<script\b[^>]*>[\s\S]*?<\/script>/gi;
90
- let match: RegExpExecArray | null = scriptRegex.exec(html);
91
-
92
- while (match) {
93
- const tag = match[0];
94
-
95
- // Check for type="module"
96
- if (/type=["']module["']/i.test(tag)) {
97
- // Extract the src value
98
- const srcMatch = tag.match(/\bsrc=["']([^"']+)["']/i);
99
- const entry = srcMatch?.[1];
100
- if (entry) {
101
- if (entry.startsWith("/")) {
102
- return entry.substring(1); // Remove leading slash
103
- }
104
- return entry;
105
- }
106
- }
107
-
108
- match = scriptRegex.exec(html);
109
- }
110
-
111
- throw new AlephaError(`No module script found in the provided HTML.`);
112
- }
113
-
114
- export const boot = {
115
- getClientEntry,
116
- getServerEntry,
117
- };