@anaemia/core 0.4.0 → 0.5.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 (76) hide show
  1. package/dist/runtime/context.d.ts.map +1 -1
  2. package/dist/runtime/context.js +4 -3
  3. package/dist/runtime/entry-client.jsx +2 -1
  4. package/dist/runtime/entry-server.d.ts +1 -2
  5. package/dist/runtime/entry-server.d.ts.map +1 -1
  6. package/dist/runtime/entry-server.jsx +18 -331
  7. package/dist/runtime/resources.d.ts.map +1 -1
  8. package/dist/runtime/resources.js +2 -1
  9. package/dist/runtime/route-data.d.ts.map +1 -1
  10. package/dist/runtime/route-data.js +4 -3
  11. package/dist/runtime/route-request.d.ts.map +1 -1
  12. package/dist/runtime/route-request.js +2 -1
  13. package/dist/runtime/rpc-client.d.ts.map +1 -1
  14. package/dist/runtime/rpc-client.js +7 -6
  15. package/dist/runtime/server/app.d.ts +22 -0
  16. package/dist/runtime/server/app.d.ts.map +1 -0
  17. package/dist/runtime/server/app.js +21 -0
  18. package/dist/runtime/server/assets.d.ts +5 -0
  19. package/dist/runtime/server/assets.d.ts.map +1 -0
  20. package/dist/runtime/server/assets.js +48 -0
  21. package/dist/runtime/server/boot.d.ts +4 -0
  22. package/dist/runtime/server/boot.d.ts.map +1 -0
  23. package/dist/runtime/server/boot.js +6 -0
  24. package/dist/runtime/server/env.d.ts +3 -0
  25. package/dist/runtime/server/env.d.ts.map +1 -0
  26. package/dist/runtime/server/env.js +14 -0
  27. package/dist/runtime/server/guards.d.ts +29 -0
  28. package/dist/runtime/server/guards.d.ts.map +1 -0
  29. package/dist/runtime/server/guards.js +12 -0
  30. package/dist/runtime/server/html.d.ts +15 -0
  31. package/dist/runtime/server/html.d.ts.map +1 -0
  32. package/dist/runtime/server/html.js +62 -0
  33. package/dist/runtime/server/hydration.d.ts +3 -0
  34. package/dist/runtime/server/hydration.d.ts.map +1 -0
  35. package/dist/runtime/server/hydration.js +20 -0
  36. package/dist/runtime/server/manifest.d.ts +14 -0
  37. package/dist/runtime/server/manifest.d.ts.map +1 -0
  38. package/dist/runtime/server/manifest.js +59 -0
  39. package/dist/runtime/server/render-request.d.ts +21 -0
  40. package/dist/runtime/server/render-request.d.ts.map +1 -0
  41. package/dist/runtime/server/render-request.jsx +170 -0
  42. package/dist/runtime/server/route-match.d.ts +14 -0
  43. package/dist/runtime/server/route-match.d.ts.map +1 -0
  44. package/dist/runtime/server/route-match.js +35 -0
  45. package/dist/runtime/server/rpc.d.ts +3 -0
  46. package/dist/runtime/server/rpc.d.ts.map +1 -0
  47. package/dist/runtime/server/rpc.js +32 -0
  48. package/dist/runtime/server/types.d.ts +33 -0
  49. package/dist/runtime/server/types.d.ts.map +1 -0
  50. package/dist/runtime/server/types.js +1 -0
  51. package/dist/runtime/shared/constants.d.ts +8 -0
  52. package/dist/runtime/shared/constants.d.ts.map +1 -0
  53. package/dist/runtime/shared/constants.js +7 -0
  54. package/package.json +5 -2
  55. package/src/runtime/context.ts +4 -6
  56. package/src/runtime/entry-client.tsx +2 -1
  57. package/src/runtime/entry-server.tsx +19 -397
  58. package/src/runtime/resources.ts +2 -1
  59. package/src/runtime/route-data.ts +5 -4
  60. package/src/runtime/route-request.ts +2 -1
  61. package/src/runtime/rpc-client.ts +8 -7
  62. package/src/runtime/server/app.ts +44 -0
  63. package/src/runtime/server/assets.ts +69 -0
  64. package/src/runtime/server/boot.ts +9 -0
  65. package/src/runtime/server/env.ts +17 -0
  66. package/src/runtime/server/guards.ts +26 -0
  67. package/src/runtime/server/html.ts +84 -0
  68. package/src/runtime/server/hydration.ts +26 -0
  69. package/src/runtime/server/manifest.ts +69 -0
  70. package/src/runtime/server/render-request.tsx +230 -0
  71. package/src/runtime/server/route-match.ts +45 -0
  72. package/src/runtime/server/rpc.ts +34 -0
  73. package/src/runtime/server/types.ts +36 -0
  74. package/src/runtime/shared/constants.ts +7 -0
  75. package/tsconfig.json +1 -1
  76. /package/{src/anaemia.d.ts → anaemia.d.ts} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boot.d.ts","sourceRoot":"","sources":["../../../src/runtime/server/boot.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,wBAAgB,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,QAIrD"}
@@ -0,0 +1,6 @@
1
+ import { serve } from "@hono/node-server";
2
+ export function serveServer(app, env) {
3
+ serve({ fetch: app.fetch, port: env.port }, (info) => {
4
+ console.log(`[anaemia framework] server live at http://localhost:${info.port}`);
5
+ });
6
+ }
@@ -0,0 +1,3 @@
1
+ import type { RuntimeEnv } from "./types.js";
2
+ export declare function createRuntimeEnv(processEnv?: NodeJS.ProcessEnv): RuntimeEnv;
3
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../src/runtime/server/env.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,wBAAgB,gBAAgB,CAAC,UAAU,GAAE,MAAM,CAAC,UAAwB,GAAG,UAAU,CAaxF"}
@@ -0,0 +1,14 @@
1
+ import path from "node:path";
2
+ export function createRuntimeEnv(processEnv = process.env) {
3
+ const port = Number(processEnv.PORT) || 3000;
4
+ const isDev = processEnv.NODE_ENV !== "production";
5
+ const devPort = Number(processEnv.RSPACK_DEV_PORT) || 4445;
6
+ return {
7
+ port,
8
+ isDev,
9
+ devServerUrl: `http://localhost:${devPort}`,
10
+ templatePath: path.resolve(process.cwd(), "./dist/client/index.html"),
11
+ manifestPath: path.resolve(process.cwd(), "./dist/route-manifest.json"),
12
+ clientDistPath: path.resolve(process.cwd(), "./dist/client"),
13
+ };
14
+ }
@@ -0,0 +1,29 @@
1
+ export type GuardFn = (ctx: {
2
+ params: Record<string, string>;
3
+ request: Request;
4
+ url: string;
5
+ }) => void | undefined | {
6
+ redirect: string;
7
+ status?: 301 | 302 | 307 | 308;
8
+ } | {
9
+ status: number;
10
+ body?: string;
11
+ } | Promise<void | undefined | {
12
+ redirect: string;
13
+ status?: number;
14
+ } | {
15
+ status: number;
16
+ body?: string;
17
+ }>;
18
+ export declare function runGuards(serverGuardRegistry: Map<string, (() => Promise<GuardFn[]>)[]>, pattern: string, ctx: {
19
+ params: Record<string, string>;
20
+ request: Request;
21
+ url: string;
22
+ }): Promise<{
23
+ status: number;
24
+ body?: string;
25
+ } | {
26
+ redirect: string;
27
+ status?: number;
28
+ } | null>;
29
+ //# sourceMappingURL=guards.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guards.d.ts","sourceRoot":"","sources":["../../../src/runtime/server/guards.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE;IAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb,KACG,IAAI,GACJ,SAAS,GACT;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;CAAE,GACpD;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACjC,OAAO,CAAC,IAAI,GAAG,SAAS,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE1G,wBAAsB,SAAS,CAC7B,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,EAC9D,OAAO,EAAE,MAAM,EACf,GAAG,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE;YAN1D,MAAM;WAAS,MAAM;;cACQ,MAAM;aAAW,MAAM;UAgBjE"}
@@ -0,0 +1,12 @@
1
+ export async function runGuards(serverGuardRegistry, pattern, ctx) {
2
+ const chain = serverGuardRegistry.get(pattern) ?? [];
3
+ for (const loadGuards of chain) {
4
+ const guards = await loadGuards();
5
+ for (const guard of guards) {
6
+ const result = await guard(ctx);
7
+ if (result && ("redirect" in result || "status" in result))
8
+ return result;
9
+ }
10
+ }
11
+ return null;
12
+ }
@@ -0,0 +1,15 @@
1
+ import type { RouteManifest } from "./types.js";
2
+ export declare function getRouteAssetTags(manifest: RouteManifest, activeChunk: string): {
3
+ scripts: string;
4
+ styles: string;
5
+ };
6
+ export declare function createDevNoCacheHeadTags(isDev: boolean): string;
7
+ export declare function createHtmlStreamShell(args: {
8
+ template: string;
9
+ headInjections: string;
10
+ bodyInjections: string;
11
+ }): {
12
+ beforeEntry: string;
13
+ afterEntry: string;
14
+ };
15
+ //# sourceMappingURL=html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../../src/runtime/server/html.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAe,aAAa,EAAE,MAAM,YAAY,CAAC;AA8B7D,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAcnH;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAI/D;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GAAG;IACjH,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB,CA2BA"}
@@ -0,0 +1,62 @@
1
+ import { ENTRY_ATTRIBUTE } from "../shared/constants.js";
2
+ const ENTRY_TAG_REGEX = /(<([a-zA-Z0-9-]+)[^>]*anaemia-entry[^>]*>)(.*?)(<\/\2>)/is;
3
+ function normalizeAssetUrl(url) {
4
+ if (!url || typeof url !== "string")
5
+ return "";
6
+ if (url.startsWith("http://") || url.startsWith("https://"))
7
+ return url;
8
+ return url.startsWith("/") ? url : `/${url}`;
9
+ }
10
+ function chunkAssetTags(chunk) {
11
+ if (!chunk)
12
+ return { scripts: "", styles: "" };
13
+ const scripts = (chunk.js ?? "")
14
+ ? (Array.isArray(chunk.js) ? chunk.js : [chunk.js])
15
+ .map((jsFile) => `<script type="module" src="${normalizeAssetUrl(jsFile)}"></script>\n`)
16
+ .join("")
17
+ : "";
18
+ const styles = (chunk.css ?? "")
19
+ ? (Array.isArray(chunk.css) ? chunk.css : [chunk.css])
20
+ .map((cssFile) => `<link rel="stylesheet" href="${normalizeAssetUrl(cssFile)}">\n`)
21
+ .join("")
22
+ : "";
23
+ return { scripts, styles };
24
+ }
25
+ export function getRouteAssetTags(manifest, activeChunk) {
26
+ const chunkNames = ["client", "commons", "vendors"];
27
+ if (activeChunk && activeChunk !== "client")
28
+ chunkNames.push(activeChunk);
29
+ return chunkNames.reduce((assetTags, chunkName) => {
30
+ const tags = chunkAssetTags(manifest.chunks[chunkName]);
31
+ return {
32
+ scripts: assetTags.scripts + tags.scripts,
33
+ styles: assetTags.styles + tags.styles,
34
+ };
35
+ }, { scripts: "", styles: "" });
36
+ }
37
+ export function createDevNoCacheHeadTags(isDev) {
38
+ return isDev
39
+ ? `<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">\n<meta http-equiv="Pragma" content="no-cache">\n<meta http-equiv="Expires" content="0">\n`
40
+ : "";
41
+ }
42
+ export function createHtmlStreamShell(args) {
43
+ const templateWithHead = args.template.replace("<head>", `<head>${args.headInjections}`);
44
+ const entryMatch = ENTRY_TAG_REGEX.exec(templateWithHead);
45
+ if (entryMatch) {
46
+ const [fullMatch, openTag, _tagName, _inner, closeTag] = entryMatch;
47
+ const beforeEntry = `${templateWithHead.slice(0, entryMatch.index)}${openTag}`;
48
+ const afterEntry = `${closeTag}${templateWithHead.slice(entryMatch.index + fullMatch.length)}`.replace("</body>", `${args.bodyInjections}</body>`);
49
+ return { beforeEntry, afterEntry };
50
+ }
51
+ const bodyCloseIndex = templateWithHead.lastIndexOf("</body>");
52
+ if (bodyCloseIndex >= 0) {
53
+ return {
54
+ beforeEntry: `${templateWithHead.slice(0, bodyCloseIndex)}<div ${ENTRY_ATTRIBUTE}>`,
55
+ afterEntry: `</div>${args.bodyInjections}${templateWithHead.slice(bodyCloseIndex)}`,
56
+ };
57
+ }
58
+ return {
59
+ beforeEntry: `${templateWithHead}<div ${ENTRY_ATTRIBUTE}>`,
60
+ afterEntry: `</div>${args.bodyInjections}`,
61
+ };
62
+ }
@@ -0,0 +1,3 @@
1
+ export declare function createHydrationDataScript(store: Map<string, unknown>): string;
2
+ export declare function createHydrationRuntimeScript(): string;
3
+ //# sourceMappingURL=hydration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hydration.d.ts","sourceRoot":"","sources":["../../../src/runtime/server/hydration.ts"],"names":[],"mappings":"AAWA,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAU7E;AAED,wBAAgB,4BAA4B,IAAI,MAAM,CAErD"}
@@ -0,0 +1,20 @@
1
+ import { generateHydrationScript } from "solid-js/web";
2
+ import { ANAEMIA_DATA_SCRIPT_ID, LOADER_DATA_KEY, SERVER_FUNCTION_DATA_KEY } from "../shared/constants.js";
3
+ function serializeJsonForHtml(value) {
4
+ return JSON.stringify(value)
5
+ .replace(/&/g, "\\u0026")
6
+ .replace(/</g, "\\u003c")
7
+ .replace(/>/g, "\\u003e")
8
+ .replace(/\//g, "\\u002f");
9
+ }
10
+ export function createHydrationDataScript(store) {
11
+ const rawStorePayload = Object.fromEntries(store);
12
+ const finalHydrationStatePayload = {
13
+ [LOADER_DATA_KEY]: rawStorePayload[LOADER_DATA_KEY] || {},
14
+ [SERVER_FUNCTION_DATA_KEY]: rawStorePayload[SERVER_FUNCTION_DATA_KEY] || {},
15
+ };
16
+ return `<script id="${ANAEMIA_DATA_SCRIPT_ID}" type="application/json">${serializeJsonForHtml(finalHydrationStatePayload)}</script>\n`;
17
+ }
18
+ export function createHydrationRuntimeScript() {
19
+ return generateHydrationScript();
20
+ }
@@ -0,0 +1,14 @@
1
+ import type { RouteManifest, RuntimeEnv } from "./types.js";
2
+ export type ManifestSnapshot = {
3
+ template: string;
4
+ manifest: RouteManifest | null;
5
+ sortedRoutes: RouteManifest["routes"];
6
+ staticRoutes: Set<string>;
7
+ loaderRoutes: Set<string>;
8
+ guardRoutes: Set<string>;
9
+ };
10
+ export declare function createManifestStore(env: RuntimeEnv): {
11
+ load: () => Promise<void>;
12
+ getSnapshot: () => ManifestSnapshot;
13
+ };
14
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/runtime/server/manifest.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG5D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;IACtC,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC1B,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,UAAU;;uBAyCzB,gBAAgB;EAczC"}
@@ -0,0 +1,59 @@
1
+ import fs from "node:fs";
2
+ import { sortRoutes } from "./route-match.js";
3
+ export function createManifestStore(env) {
4
+ let memoizedHtmlTemplate = "";
5
+ let memoizedManifest = null;
6
+ let memoizedSortedRoutes = [];
7
+ const load = async () => {
8
+ if (env.isDev) {
9
+ try {
10
+ memoizedHtmlTemplate = await fetch(`${env.devServerUrl}/index.html`).then((response) => {
11
+ if (!response.ok)
12
+ throw new Error(`index.html fetch failed: ${response.status}`);
13
+ return response.text();
14
+ });
15
+ }
16
+ catch (err) {
17
+ console.error("[anaemia engine sync error - HTML fetch failed]:", err);
18
+ memoizedHtmlTemplate = "";
19
+ }
20
+ try {
21
+ if (fs.existsSync(env.manifestPath)) {
22
+ memoizedManifest = JSON.parse(fs.readFileSync(env.manifestPath, "utf-8"));
23
+ }
24
+ else {
25
+ memoizedManifest = { routes: [], chunks: {}, errors: {} };
26
+ }
27
+ }
28
+ catch (err) {
29
+ console.error("[anaemia engine sync error - manifest read failed]:", err);
30
+ memoizedManifest = { routes: [], chunks: {}, errors: {} };
31
+ }
32
+ }
33
+ else {
34
+ try {
35
+ if (fs.existsSync(env.templatePath))
36
+ memoizedHtmlTemplate = fs.readFileSync(env.templatePath, "utf-8");
37
+ if (fs.existsSync(env.manifestPath)) {
38
+ memoizedManifest = JSON.parse(fs.readFileSync(env.manifestPath, "utf-8"));
39
+ }
40
+ }
41
+ catch {
42
+ console.warn("build assets not fully initialized during bootstrapping cycle.");
43
+ }
44
+ }
45
+ memoizedSortedRoutes = memoizedManifest ? sortRoutes(memoizedManifest.routes) : [];
46
+ };
47
+ const getSnapshot = () => {
48
+ const routes = memoizedManifest?.routes ?? [];
49
+ return {
50
+ template: memoizedHtmlTemplate,
51
+ manifest: memoizedManifest,
52
+ sortedRoutes: memoizedSortedRoutes,
53
+ staticRoutes: new Set(routes.filter((r) => r.isStatic).map((r) => r.urlPattern)),
54
+ loaderRoutes: new Set(routes.filter((r) => r.hasLoader).map((r) => r.urlPattern)),
55
+ guardRoutes: new Set(routes.filter((r) => r.hasGuard).map((r) => r.urlPattern)),
56
+ };
57
+ };
58
+ return { load, getSnapshot };
59
+ }
@@ -0,0 +1,21 @@
1
+ import type { Component } from "solid-js";
2
+ import type { Context } from "hono";
3
+ import { type GuardFn } from "./guards.js";
4
+ import type { RuntimeEnv } from "./types.js";
5
+ import type { ManifestSnapshot } from "./manifest.js";
6
+ type ServerLoader = (args: {
7
+ params: Record<string, string>;
8
+ request: Request;
9
+ }) => unknown | Promise<unknown>;
10
+ type RenderRequestOptions = {
11
+ App: Component;
12
+ env: RuntimeEnv;
13
+ preloadActiveClientRoute: (path: string) => unknown | Promise<unknown>;
14
+ serverLoaderRegistry: Map<string, ServerLoader>;
15
+ serverGuardRegistry: Map<string, (() => Promise<GuardFn[]>)[]>;
16
+ getManifestSnapshot: () => ManifestSnapshot;
17
+ loadManifestAndTemplate: () => Promise<void>;
18
+ };
19
+ export declare function createRenderRequestHandler(options: RenderRequestOptions): (c: Context) => Promise<Response>;
20
+ export {};
21
+ //# sourceMappingURL=render-request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-request.d.ts","sourceRoot":"","sources":["../../../src/runtime/server/render-request.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAMpC,OAAO,EAAa,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AAItD,OAAO,KAAK,EAAiB,UAAU,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAItD,KAAK,YAAY,GAAG,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAE/G,KAAK,oBAAoB,GAAG;IAC1B,GAAG,EAAE,SAAS,CAAC;IACf,GAAG,EAAE,UAAU,CAAC;IAChB,wBAAwB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvE,oBAAoB,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAChD,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAC/D,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;IAC5C,uBAAuB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C,CAAC;AAmGF,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,oBAAoB,IACxD,GAAG,OAAO,uBAsGzB"}
@@ -0,0 +1,170 @@
1
+ import { Router } from "@solidjs/router";
2
+ import { renderToStream } from "solid-js/web";
3
+ import { ssrStorage } from "../context.js";
4
+ import { LOADER_DATA_KEY } from "../shared/constants.js";
5
+ import { setDevResponseCacheHeaders } from "./assets.js";
6
+ import { runGuards } from "./guards.js";
7
+ import { createDevNoCacheHeadTags, createHtmlStreamShell, getRouteAssetTags } from "./html.js";
8
+ import { createHydrationDataScript, createHydrationRuntimeScript } from "./hydration.js";
9
+ import { matchRoute } from "./route-match.js";
10
+ const staticCache = new Map();
11
+ function renderApp(App, url) {
12
+ return renderToStream(() => (<Router url={url}>
13
+ <App />
14
+ </Router>));
15
+ }
16
+ async function render500(args) {
17
+ const error500Pattern = args.manifest.errors?.["500"];
18
+ if (!error500Pattern) {
19
+ const stack = args.error instanceof Error ? args.error.stack : String(args.error);
20
+ return `<h1>500 Internal Server Error</h1><pre>${args.env.isDev ? stack : ""}</pre>`;
21
+ }
22
+ const error500Loader = args.serverLoaderRegistry.get(error500Pattern);
23
+ if (error500Loader) {
24
+ const message = args.error instanceof Error ? args.error.message : String(args.error);
25
+ const stack = args.error instanceof Error ? args.error.stack : undefined;
26
+ args.store.set(LOADER_DATA_KEY, { message, stack: args.env.isDev ? stack : undefined });
27
+ try {
28
+ return await ssrStorage.run(args.store, async () => renderApp(args.App, error500Pattern));
29
+ }
30
+ catch {
31
+ return `<h1>500 Internal Server Error</h1>`;
32
+ }
33
+ }
34
+ const stack = args.error instanceof Error ? args.error.stack : String(args.error);
35
+ return `<h1>500 Internal Server Error</h1><pre>${args.env.isDev ? stack : ""}</pre>`;
36
+ }
37
+ function createHtmlResponseStream(args) {
38
+ const encoder = new TextEncoder();
39
+ const collected = [];
40
+ const shouldCollect = Boolean(args.onComplete);
41
+ return new ReadableStream({
42
+ start(controller) {
43
+ const enqueue = (chunk) => {
44
+ if (shouldCollect)
45
+ collected.push(chunk);
46
+ controller.enqueue(encoder.encode(chunk));
47
+ };
48
+ enqueue(args.beforeEntry);
49
+ if (typeof args.renderStream === "string") {
50
+ enqueue(args.renderStream);
51
+ const after = args.afterEntry();
52
+ enqueue(after);
53
+ args.onComplete?.(collected.join(""));
54
+ controller.close();
55
+ return;
56
+ }
57
+ const renderStream = args.renderStream;
58
+ const writable = new WritableStream({
59
+ write(chunk) {
60
+ enqueue(chunk);
61
+ },
62
+ close() {
63
+ const after = args.afterEntry();
64
+ enqueue(after);
65
+ args.onComplete?.(collected.join(""));
66
+ controller.close();
67
+ },
68
+ abort(error) {
69
+ controller.error(error);
70
+ },
71
+ });
72
+ void ssrStorage
73
+ .run(args.store, async () => {
74
+ await renderStream.pipeTo(writable);
75
+ })
76
+ .catch((error) => {
77
+ controller.error(error);
78
+ });
79
+ },
80
+ });
81
+ }
82
+ export function createRenderRequestHandler(options) {
83
+ return async (c) => {
84
+ if (options.env.isDev)
85
+ await options.loadManifestAndTemplate();
86
+ const { template, manifest, sortedRoutes, staticRoutes, loaderRoutes, guardRoutes } = options.getManifestSnapshot();
87
+ if (!template || !manifest) {
88
+ return c.text("anaemia engine error: build assets are missing", 500);
89
+ }
90
+ const reqPath = c.req.path;
91
+ const { activeChunk, targetPattern, statusCode: matchedStatus, params, } = matchRoute(manifest, reqPath, sortedRoutes);
92
+ let statusCode = matchedStatus;
93
+ const loaderArgs = { params, request: c.req.raw };
94
+ const store = ssrStorage.getStore() || new Map();
95
+ let renderStream;
96
+ const isStaticRoute = !options.env.isDev && staticRoutes.has(targetPattern);
97
+ // serve from cache before doing any work
98
+ if (isStaticRoute) {
99
+ const cached = staticCache.get(reqPath);
100
+ if (cached) {
101
+ c.header("Content-Type", "text/html; charset=UTF-8");
102
+ c.header("X-Anaemia-Cache", "HIT");
103
+ return c.html(cached, statusCode);
104
+ }
105
+ }
106
+ if (targetPattern && guardRoutes.has(targetPattern)) {
107
+ try {
108
+ const guardResult = await runGuards(options.serverGuardRegistry, targetPattern, {
109
+ params,
110
+ request: c.req.raw,
111
+ url: reqPath,
112
+ });
113
+ if (guardResult) {
114
+ if ("redirect" in guardResult) {
115
+ return c.redirect(guardResult.redirect, (guardResult.status ?? 302));
116
+ }
117
+ if ("status" in guardResult)
118
+ statusCode = guardResult.status;
119
+ }
120
+ }
121
+ catch (err) {
122
+ console.error("[anaemia] guard threw unexpectedly:", err);
123
+ return c.text("Internal Server Error", 500);
124
+ }
125
+ }
126
+ try {
127
+ renderStream = await ssrStorage.run(store, async () => {
128
+ if (targetPattern && loaderRoutes.has(targetPattern)) {
129
+ const executableLoader = options.serverLoaderRegistry.get(targetPattern);
130
+ if (executableLoader) {
131
+ const initialLoaderData = await executableLoader(loaderArgs);
132
+ store.set(LOADER_DATA_KEY, initialLoaderData);
133
+ }
134
+ }
135
+ await options.preloadActiveClientRoute(reqPath);
136
+ return renderApp(options.App, reqPath);
137
+ });
138
+ }
139
+ catch (err) {
140
+ statusCode = 500;
141
+ console.error("[anaemia framework] runtime execution crash handled:", err);
142
+ renderStream = await render500({
143
+ App: options.App,
144
+ error: err,
145
+ env: options.env,
146
+ manifest,
147
+ serverLoaderRegistry: options.serverLoaderRegistry,
148
+ store,
149
+ });
150
+ }
151
+ const routeAssetTags = getRouteAssetTags(manifest, activeChunk);
152
+ const headInjections = `${createDevNoCacheHeadTags(options.env.isDev)}${routeAssetTags.styles}${createHydrationRuntimeScript()}`;
153
+ const shell = createHtmlStreamShell({ template, headInjections, bodyInjections: "" });
154
+ setDevResponseCacheHeaders(c, options.env);
155
+ c.status(statusCode);
156
+ c.header("Content-Type", "text/html; charset=UTF-8");
157
+ return c.body(createHtmlResponseStream({
158
+ beforeEntry: shell.beforeEntry,
159
+ renderStream,
160
+ afterEntry: () => {
161
+ const bodyInjections = `${createHydrationDataScript(store)}${routeAssetTags.scripts}`;
162
+ return shell.afterEntry.includes("</body>")
163
+ ? shell.afterEntry.replace("</body>", `${bodyInjections}</body>`)
164
+ : `${shell.afterEntry}${bodyInjections}`;
165
+ },
166
+ store,
167
+ onComplete: isStaticRoute ? (html) => staticCache.set(reqPath, html) : undefined,
168
+ }));
169
+ };
170
+ }
@@ -0,0 +1,14 @@
1
+ import type { RouteManifest, RouteMatch } from "./types.js";
2
+ type SortedRoute = RouteManifest["routes"][number];
3
+ export declare function sortRoutes(routes: RouteManifest["routes"]): SortedRoute[];
4
+ export declare function matchRoute(manifest: RouteManifest, reqPath: string, sortedRoutes?: {
5
+ urlPattern: string;
6
+ chunkName: string;
7
+ params: string[];
8
+ isStatic: boolean;
9
+ hasLoader: boolean;
10
+ hasGuard: boolean;
11
+ serverFunctionIds: string[];
12
+ }[]): RouteMatch;
13
+ export {};
14
+ //# sourceMappingURL=route-match.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-match.d.ts","sourceRoot":"","sources":["../../../src/runtime/server/route-match.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE5D,KAAK,WAAW,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAWnD,wBAAgB,UAAU,CAAC,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,GAAG,WAAW,EAAE,CAEzE;AAED,wBAAgB,UAAU,CACxB,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,MAAM,EACf,YAAY;;;;;;;;GAA8B,GACzC,UAAU,CAuBZ"}
@@ -0,0 +1,35 @@
1
+ function scoreRoutePattern(pattern) {
2
+ const segments = pattern.split("/").filter(Boolean);
3
+ return segments.reduce((acc, segment) => {
4
+ if (segment.startsWith(":"))
5
+ return acc - 1;
6
+ if (segment === "*" || segment.startsWith("*"))
7
+ return acc - 2;
8
+ return acc;
9
+ }, segments.length * 10);
10
+ }
11
+ export function sortRoutes(routes) {
12
+ return [...routes].sort((a, b) => scoreRoutePattern(b.urlPattern) - scoreRoutePattern(a.urlPattern));
13
+ }
14
+ export function matchRoute(manifest, reqPath, sortedRoutes = sortRoutes(manifest.routes)) {
15
+ for (const route of sortedRoutes) {
16
+ const regexStr = route.urlPattern
17
+ .replace(/:([a-zA-Z0-9_-]+)/g, "(?<$1>[^/]+)")
18
+ .replace(/\*([a-zA-Z0-9_-]*)/g, "(?<catchall>.*)");
19
+ const match = new RegExp(`^${regexStr}$`).exec(reqPath);
20
+ if (match) {
21
+ return {
22
+ activeChunk: route.chunkName,
23
+ targetPattern: route.urlPattern,
24
+ statusCode: 200,
25
+ params: match.groups ? { ...match.groups } : {},
26
+ };
27
+ }
28
+ }
29
+ return {
30
+ activeChunk: "route-404",
31
+ targetPattern: manifest.errors?.["404"] || "",
32
+ statusCode: 404,
33
+ params: {},
34
+ };
35
+ }
@@ -0,0 +1,3 @@
1
+ import type { Hono } from "hono";
2
+ export declare function registerRpcRoute(app: Hono): void;
3
+ //# sourceMappingURL=rpc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc.d.ts","sourceRoot":"","sources":["../../../src/runtime/server/rpc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAIjC,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,IAAI,QA6BzC"}
@@ -0,0 +1,32 @@
1
+ import { serverFunctionsRegistry } from "../context.js";
2
+ import { RPC_PATH } from "../shared/constants.js";
3
+ export function registerRpcRoute(app) {
4
+ app.post(RPC_PATH, async (c) => {
5
+ const functionId = c.req.query("id");
6
+ if (!functionId || !serverFunctionsRegistry.has(functionId)) {
7
+ return c.json({ error: "RPC function not found" }, 404);
8
+ }
9
+ const contentLength = Number(c.req.header("content-length") ?? 0);
10
+ if (contentLength > 512_000) {
11
+ return c.json({ error: "Payload too large" }, 413);
12
+ }
13
+ let argumentsArray;
14
+ try {
15
+ const body = await c.req.json();
16
+ if (!Array.isArray(body))
17
+ throw new Error("Expected array");
18
+ argumentsArray = body;
19
+ }
20
+ catch {
21
+ return c.json({ error: "Invalid request body" }, 400);
22
+ }
23
+ try {
24
+ const result = await serverFunctionsRegistry.get(functionId)(...argumentsArray);
25
+ return c.json(result);
26
+ }
27
+ catch (error) {
28
+ const message = error instanceof Error ? error.message : "Internal server error";
29
+ return c.json({ error: message }, 500);
30
+ }
31
+ });
32
+ }
@@ -0,0 +1,33 @@
1
+ import type { StatusCode } from "hono/utils/http-status";
2
+ export interface ChunkAssets {
3
+ js?: string[];
4
+ css?: string[];
5
+ }
6
+ export type RouteManifest = {
7
+ routes: Array<{
8
+ urlPattern: string;
9
+ chunkName: string;
10
+ params: string[];
11
+ isStatic: boolean;
12
+ hasLoader: boolean;
13
+ hasGuard: boolean;
14
+ serverFunctionIds: string[];
15
+ }>;
16
+ chunks: Record<string, ChunkAssets>;
17
+ errors?: Record<string, string>;
18
+ };
19
+ export type RouteMatch = {
20
+ activeChunk: string;
21
+ targetPattern: string;
22
+ statusCode: StatusCode;
23
+ params: Record<string, string>;
24
+ };
25
+ export type RuntimeEnv = {
26
+ port: number;
27
+ isDev: boolean;
28
+ devServerUrl: string;
29
+ templatePath: string;
30
+ manifestPath: string;
31
+ clientDistPath: string;
32
+ };
33
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/runtime/server/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD,MAAM,WAAW,WAAW;IAC1B,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,KAAK,CAAC;QACZ,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,QAAQ,EAAE,OAAO,CAAC;QAClB,SAAS,EAAE,OAAO,CAAC;QACnB,QAAQ,EAAE,OAAO,CAAC;QAClB,iBAAiB,EAAE,MAAM,EAAE,CAAC;KAC7B,CAAC,CAAC;IACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ export declare const ANAEMIA_DATA_SCRIPT_ID = "__ANAEMIA_DATA__";
2
+ export declare const ENTRY_SELECTOR = "[anaemia-entry]";
3
+ export declare const ENTRY_ATTRIBUTE = "anaemia-entry";
4
+ export declare const HONO_CONTEXT_KEY = "honoContext";
5
+ export declare const LOADER_DATA_KEY = "__LOADER_DATA__";
6
+ export declare const RPC_PATH = "/_rpc";
7
+ export declare const SERVER_FUNCTION_DATA_KEY = "__SERVER_FUNCTION_DATA__";
8
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/runtime/shared/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AACzD,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAChD,eAAO,MAAM,eAAe,kBAAkB,CAAC;AAC/C,eAAO,MAAM,gBAAgB,gBAAgB,CAAC;AAC9C,eAAO,MAAM,eAAe,oBAAoB,CAAC;AACjD,eAAO,MAAM,QAAQ,UAAU,CAAC;AAChC,eAAO,MAAM,wBAAwB,6BAA6B,CAAC"}
@@ -0,0 +1,7 @@
1
+ export const ANAEMIA_DATA_SCRIPT_ID = "__ANAEMIA_DATA__";
2
+ export const ENTRY_SELECTOR = "[anaemia-entry]";
3
+ export const ENTRY_ATTRIBUTE = "anaemia-entry";
4
+ export const HONO_CONTEXT_KEY = "honoContext";
5
+ export const LOADER_DATA_KEY = "__LOADER_DATA__";
6
+ export const RPC_PATH = "/_rpc";
7
+ export const SERVER_FUNCTION_DATA_KEY = "__SERVER_FUNCTION_DATA__";