@anaemia/core 0.3.7 → 0.5.0

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 (94) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +1 -1
  3. package/dist/config.d.ts +8 -1
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/plugins/lightningcss.js +4 -4
  8. package/dist/runtime/context.d.ts.map +1 -1
  9. package/dist/runtime/context.js +5 -4
  10. package/dist/runtime/entry-client.jsx +4 -5
  11. package/dist/runtime/entry-server.d.ts +1 -2
  12. package/dist/runtime/entry-server.d.ts.map +1 -1
  13. package/dist/runtime/entry-server.jsx +18 -321
  14. package/dist/runtime/resources.d.ts.map +1 -1
  15. package/dist/runtime/resources.js +2 -1
  16. package/dist/runtime/route-data.d.ts.map +1 -1
  17. package/dist/runtime/route-data.js +4 -3
  18. package/dist/runtime/route-request.d.ts.map +1 -1
  19. package/dist/runtime/route-request.js +3 -2
  20. package/dist/runtime/rpc-client.d.ts.map +1 -1
  21. package/dist/runtime/rpc-client.js +10 -9
  22. package/dist/runtime/server/app.d.ts +22 -0
  23. package/dist/runtime/server/app.d.ts.map +1 -0
  24. package/dist/runtime/server/app.js +21 -0
  25. package/dist/runtime/server/assets.d.ts +5 -0
  26. package/dist/runtime/server/assets.d.ts.map +1 -0
  27. package/dist/runtime/server/assets.js +48 -0
  28. package/dist/runtime/server/boot.d.ts +4 -0
  29. package/dist/runtime/server/boot.d.ts.map +1 -0
  30. package/dist/runtime/server/boot.js +6 -0
  31. package/dist/runtime/server/env.d.ts +3 -0
  32. package/dist/runtime/server/env.d.ts.map +1 -0
  33. package/dist/runtime/server/env.js +14 -0
  34. package/dist/runtime/server/guards.d.ts +29 -0
  35. package/dist/runtime/server/guards.d.ts.map +1 -0
  36. package/dist/runtime/server/guards.js +12 -0
  37. package/dist/runtime/server/html.d.ts +15 -0
  38. package/dist/runtime/server/html.d.ts.map +1 -0
  39. package/dist/runtime/server/html.js +62 -0
  40. package/dist/runtime/server/hydration.d.ts +3 -0
  41. package/dist/runtime/server/hydration.d.ts.map +1 -0
  42. package/dist/runtime/server/hydration.js +20 -0
  43. package/dist/runtime/server/manifest.d.ts +14 -0
  44. package/dist/runtime/server/manifest.d.ts.map +1 -0
  45. package/dist/runtime/server/manifest.js +59 -0
  46. package/dist/runtime/server/render-request.d.ts +21 -0
  47. package/dist/runtime/server/render-request.d.ts.map +1 -0
  48. package/dist/runtime/server/render-request.jsx +170 -0
  49. package/dist/runtime/server/route-match.d.ts +14 -0
  50. package/dist/runtime/server/route-match.d.ts.map +1 -0
  51. package/dist/runtime/server/route-match.js +35 -0
  52. package/dist/runtime/server/rpc.d.ts +3 -0
  53. package/dist/runtime/server/rpc.d.ts.map +1 -0
  54. package/dist/runtime/server/rpc.js +32 -0
  55. package/dist/runtime/server/types.d.ts +33 -0
  56. package/dist/runtime/server/types.d.ts.map +1 -0
  57. package/dist/runtime/server/types.js +1 -0
  58. package/dist/runtime/shared/constants.d.ts +8 -0
  59. package/dist/runtime/shared/constants.d.ts.map +1 -0
  60. package/dist/runtime/shared/constants.js +7 -0
  61. package/dist/types.d.ts.map +1 -1
  62. package/package.json +11 -7
  63. package/src/anaemia.d.ts +75 -0
  64. package/src/config.ts +9 -1
  65. package/src/index.ts +9 -1
  66. package/src/plugins/index.ts +1 -1
  67. package/src/plugins/lightningcss.ts +11 -11
  68. package/src/runtime/context.browser.ts +1 -1
  69. package/src/runtime/context.ts +6 -5
  70. package/src/runtime/entry-client.tsx +5 -8
  71. package/src/runtime/entry-server.tsx +19 -373
  72. package/src/runtime/resources.ts +3 -2
  73. package/src/runtime/route-data.ts +6 -5
  74. package/src/runtime/route-request.ts +4 -3
  75. package/src/runtime/rpc-client.ts +14 -13
  76. package/src/runtime/server/app.ts +44 -0
  77. package/src/runtime/server/assets.ts +69 -0
  78. package/src/runtime/server/boot.ts +9 -0
  79. package/src/runtime/server/env.ts +17 -0
  80. package/src/runtime/server/guards.ts +26 -0
  81. package/src/runtime/server/html.ts +84 -0
  82. package/src/runtime/server/hydration.ts +26 -0
  83. package/src/runtime/server/manifest.ts +69 -0
  84. package/src/runtime/server/render-request.tsx +230 -0
  85. package/src/runtime/server/route-match.ts +45 -0
  86. package/src/runtime/server/rpc.ts +34 -0
  87. package/src/runtime/server/types.ts +36 -0
  88. package/src/runtime/shared/constants.ts +7 -0
  89. package/src/runtime/webpack.d.ts +1 -1
  90. package/src/types.ts +18 -3
  91. package/test/integration/hmr.test.mjs +18 -5
  92. package/test/rpc-client.test.mjs +1 -1
  93. package/test/run-on-server.test.mjs +2 -6
  94. package/tsconfig.json +1 -1
package/LICENSE CHANGED
@@ -186,7 +186,7 @@
186
186
  same "printed page" as the copyright notice for easier
187
187
  identification within third-party archives.
188
188
 
189
- Copyright [yyyy] [name of copyright owner]
189
+ Copyright [2026] [colourlabs]
190
190
 
191
191
  Licensed under the Apache License, Version 2.0 (the "License");
192
192
  you may not use this file except in compliance with the License.
package/README.md CHANGED
@@ -1,3 +1,3 @@
1
1
  # @anaemia/core
2
2
 
3
- the core runtime for the [anaemia](https://github.com/colourlabs/anaemia) SolidJS SSR framework
3
+ the core runtime for the [anaemia](https://github.com/colourlabs/anaemia) SolidJS SSR framework
package/dist/config.d.ts CHANGED
@@ -38,6 +38,13 @@ export interface AnaemiaConfig {
38
38
  styles?: {
39
39
  sass?: boolean;
40
40
  modules?: boolean;
41
+ /**
42
+ * customize the generated CSS module class names.
43
+ * defaults to "[name]__[local]__[hash:base64:5]" in both development and production
44
+ * for readable, themeable class names. Set to "[hash:base64:8]" if you prefer
45
+ * fully hashed production output and don't need external theme support.
46
+ */
47
+ modulesLocalIdentName?: string;
41
48
  };
42
49
  experimental?: {
43
50
  outputModule?: boolean;
@@ -54,7 +61,7 @@ export interface AnaemiaConfig {
54
61
  * client: { __APP_VERSION__: JSON.stringify("1.0.0") },
55
62
  * server: { __DB_POOL_SIZE__: "10" }
56
63
  * }
57
- */
64
+ */
58
65
  define?: {
59
66
  client?: Record<string, string>;
60
67
  server?: Record<string, string>;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,aAAa,CAAC;IAE9D;;OAEG;IACH,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,aAAa,CAAC;IAE9D;;OAEG;IACH,YAAY,CAAC,EAAE;QACb,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;QACtB,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;KACvB,CAAC;IAEF;;OAEG;IACH,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,IAAI,CAAC;IAEtC;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE;QACP,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,MAAM,CAAC,EAAE;QACP,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC;IACF,YAAY,CAAC,EAAE;QACb,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,CAAC;IAEF;;OAEG;IACH,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAE1B;;;;;;;;MAQE;IACF,MAAM,CAAC,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACjC,CAAC;CACH;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CAEjE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,aAAa,CAAC;IAE9D;;OAEG;IACH,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,aAAa,CAAC;IAE9D;;OAEG;IACH,YAAY,CAAC,EAAE;QACb,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;QACtB,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;KACvB,CAAC;IAEF;;OAEG;IACH,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,IAAI,CAAC;IAEtC;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE;QACP,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,MAAM,CAAC,EAAE;QACP,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,OAAO,CAAC,EAAE,OAAO,CAAC;QAElB;;;;;WAKG;QACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;KAChC,CAAC;IACF,YAAY,CAAC,EAAE;QACb,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,CAAC;IAEF;;OAEG;IACH,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAE1B;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACjC,CAAC;CACH;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CAEjE"}
package/dist/index.d.ts CHANGED
@@ -3,5 +3,5 @@ export { runOnServer } from "./runtime/context.js";
3
3
  export { RouteDataController, useRouteData } from "./runtime/route-data.js";
4
4
  export { $$executeClientRpc } from "./runtime/rpc-client.js";
5
5
  export { createServerResource } from "./runtime/resources.js";
6
- export type { LoaderArgs, LoaderFunction, InferServerData, GuardContext, GuardResult, GuardFn, ServerFunction } from "./types.js";
6
+ export type { LoaderArgs, LoaderFunction, InferServerData, GuardContext, GuardResult, GuardFn, ServerFunction, } from "./types.js";
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAE5B,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE9D,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAE5B,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE9D,YAAY,EACV,UAAU,EACV,cAAc,EACd,eAAe,EACf,YAAY,EACZ,WAAW,EACX,OAAO,EACP,cAAc,GACf,MAAM,YAAY,CAAC"}
@@ -14,7 +14,7 @@ export function anaemiaLightningCssPlugin(options = {}) {
14
14
  clientRspackConfig(config) {
15
15
  const isProd = process.env.NODE_ENV === "production";
16
16
  if (config.module?.rules) {
17
- config.module.rules.forEach((rule) => {
17
+ for (const rule of config.module.rules) {
18
18
  if (rule && typeof rule === "object" && rule.test && rule.test.toString().includes("ss")) {
19
19
  const currentUse = Array.isArray(rule.use) ? rule.use : [];
20
20
  rule.use = [
@@ -28,7 +28,7 @@ export function anaemiaLightningCssPlugin(options = {}) {
28
28
  },
29
29
  ];
30
30
  }
31
- });
31
+ }
32
32
  }
33
33
  if (isProd) {
34
34
  config.optimization = {
@@ -46,7 +46,7 @@ export function anaemiaLightningCssPlugin(options = {}) {
46
46
  },
47
47
  serverRspackConfig(config) {
48
48
  if (config.module?.rules) {
49
- config.module.rules.forEach((rule) => {
49
+ for (const rule of config.module.rules) {
50
50
  if (rule && typeof rule === "object" && rule.test && rule.test.toString().includes("ss")) {
51
51
  const currentUse = Array.isArray(rule.use) ? rule.use : [];
52
52
  rule.use = [
@@ -60,7 +60,7 @@ export function anaemiaLightningCssPlugin(options = {}) {
60
60
  },
61
61
  ];
62
62
  }
63
- });
63
+ }
64
64
  }
65
65
  return config;
66
66
  },
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/runtime/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,KAAK,KAAK,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;AAE7C,eAAO,MAAM,uBAAuB,oBAA2B,CAAC;AAChE,eAAO,MAAM,UAAU,yCAAgD,CAAC;AAGxE,wBAAgB,WAAW,CAAC,CAAC,SAAS,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAsB1F"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/runtime/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD,KAAK,KAAK,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;AAE7C,eAAO,MAAM,uBAAuB,oBAA2B,CAAC;AAChE,eAAO,MAAM,UAAU,yCAAgD,CAAC;AAGxE,wBAAgB,WAAW,CAAC,CAAC,SAAS,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAsB1F"}
@@ -1,18 +1,19 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { SERVER_FUNCTION_DATA_KEY } from "./shared/constants.js";
2
3
  export const serverFunctionsRegistry = new Map();
3
4
  export const ssrStorage = new AsyncLocalStorage();
4
5
  globalThis.__ANAEMIA_SERVER_STORAGE__ = ssrStorage;
5
6
  export function runOnServer(backendFn, id) {
6
- const hashId = id || "";
7
+ const hashId = id ?? crypto.randomUUID();
7
8
  serverFunctionsRegistry.set(hashId, backendFn);
8
9
  const rpcProxy = async function (...args) {
9
10
  const result = await backendFn(...args);
10
11
  const store = ssrStorage.getStore();
11
12
  if (store && hashId) {
12
- if (!store.has("__SERVER_FUNCTION_DATA__")) {
13
- store.set("__SERVER_FUNCTION_DATA__", {});
13
+ if (!store.has(SERVER_FUNCTION_DATA_KEY)) {
14
+ store.set(SERVER_FUNCTION_DATA_KEY, {});
14
15
  }
15
- const functionCache = store.get("__SERVER_FUNCTION_DATA__");
16
+ const functionCache = store.get(SERVER_FUNCTION_DATA_KEY);
16
17
  if (!functionCache[hashId]) {
17
18
  functionCache[hashId] = {};
18
19
  }
@@ -1,19 +1,18 @@
1
1
  import { hydrate, render } from "solid-js/web";
2
2
  import { Router } from "@solidjs/router";
3
+ import { ENTRY_SELECTOR } from "./shared/constants.js";
3
4
  // @ts-expect-error - resolved by Rspack
4
5
  import App, { preloadActiveClientRoute } from "anaemia-user-app";
5
- const mountTarget = document.querySelector("[anaemia-entry]");
6
+ const mountTarget = document.querySelector(ENTRY_SELECTOR);
6
7
  if (!mountTarget) {
7
8
  throw new Error("[anaemia] missing mount target");
8
9
  }
9
10
  const root = mountTarget;
10
11
  async function start() {
11
12
  await preloadActiveClientRoute(window.location.pathname);
12
- const mount = root.hasChildNodes()
13
- ? hydrate
14
- : render;
13
+ const mount = root.hasChildNodes() ? hydrate : render;
15
14
  mount(() => (<Router>
16
15
  <App />
17
16
  </Router>), root);
18
17
  }
19
- start();
18
+ await start();
@@ -1,4 +1,3 @@
1
- import { Hono } from "hono";
2
- declare const app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
1
+ declare const app: import("hono").Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
3
2
  export default app;
4
3
  //# sourceMappingURL=entry-server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"entry-server.d.ts","sourceRoot":"","sources":["../../src/runtime/entry-server.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AA2C5B,QAAA,MAAM,GAAG,4EAAa,CAAC;AA6VvB,eAAe,GAAG,CAAC"}
1
+ {"version":3,"file":"entry-server.d.ts","sourceRoot":"","sources":["../../src/runtime/entry-server.tsx"],"names":[],"mappings":"AAiBA,QAAA,MAAM,GAAG,2FASP,CAAC;AAYH,eAAe,GAAG,CAAC"}
@@ -1,332 +1,29 @@
1
- import { Hono } from "hono";
2
- import { serve } from "@hono/node-server";
3
- import { serveStatic } from "@hono/node-server/serve-static";
4
- import { compress } from "hono/compress";
5
- import { renderToStringAsync, generateHydrationScript } from "solid-js/web";
6
- import { Router } from "@solidjs/router";
7
- import { ssrStorage, serverFunctionsRegistry } from "./context.js";
8
- import fs from "node:fs";
9
- import path from "path";
1
+ import { createServerApp } from "./server/app.js";
2
+ import { serveServer } from "./server/boot.js";
3
+ import { createRuntimeEnv } from "./server/env.js";
4
+ import { createManifestStore } from "./server/manifest.js";
10
5
  // @ts-expect-error - resolved by Rspack
11
6
  import App from "anaemia-user-app";
12
7
  // @ts-expect-error - resolved by Rspack
13
8
  import { preloadActiveClientRoute, serverLoaderRegistry, serverGuardRegistry } from "anaemia-user-app";
14
9
  // @ts-expect-error - resolved by Rspack
15
10
  import { registerServerRoutes } from "__anaemia_server_routes__";
16
- const port = Number(process.env.PORT) || 3000;
17
- const isDev = process.env.NODE_ENV !== "production";
18
- const devPort = Number(process.env.RSPACK_DEV_PORT) || 4445;
19
- const devServerUrl = `http://localhost:${devPort}`;
20
- let sortedRoutes = null;
21
- const ENTRY_TAG_REGEX = /(<([a-zA-Z0-9-]+)[^>]*anaemia-entry[^>]*>)(.*?)(<\/\2>)/is;
22
- const app = new Hono();
23
- app.use("*", compress());
24
- app.use("*", async (c, next) => {
25
- const store = new Map();
26
- store.set("honoContext", c);
27
- return await ssrStorage.run(store, next);
11
+ const env = createRuntimeEnv();
12
+ const manifestStore = createManifestStore(env);
13
+ const app = createServerApp({
14
+ App,
15
+ env,
16
+ preloadActiveClientRoute,
17
+ serverLoaderRegistry,
18
+ serverGuardRegistry,
19
+ registerServerRoutes,
20
+ getManifestSnapshot: manifestStore.getSnapshot,
21
+ loadManifestAndTemplate: manifestStore.load,
28
22
  });
29
- if (isDev) {
30
- const devAssetProxy = async (c) => {
31
- const targetUrl = `${devServerUrl}${c.req.path}`;
32
- try {
33
- const response = await fetch(targetUrl);
34
- if (!response.ok)
35
- return c.text("asset not found in Rspack memory", 404);
36
- const contentType = response.headers.get("content-type");
37
- if (contentType)
38
- c.header("content-type", contentType);
39
- c.header("Cache-Control", "no-cache, no-store, must-revalidate");
40
- c.header("Pragma", "no-cache");
41
- c.header("Expires", "0");
42
- return c.body(await response.arrayBuffer());
43
- }
44
- catch {
45
- return c.text("failed to connect to Rspack dev server asset bridge", 500);
46
- }
47
- };
48
- app.get("/assets/*", devAssetProxy);
49
- }
50
- else {
51
- app.use("/assets/*", async (c, next) => {
52
- await next();
53
- if (c.res.ok)
54
- c.res.headers.set("Cache-Control", "public, max-age=31536000, immutable");
55
- });
56
- app.use("/assets/*", serveStatic({
57
- root: path.resolve(process.cwd(), "./dist/client"),
58
- }));
59
- }
60
- app.post("/_rpc", async (c) => {
61
- const functionId = c.req.query("id");
62
- if (!functionId || !serverFunctionsRegistry.has(functionId)) {
63
- return c.json({ error: "RPC function not found" }, 404);
64
- }
65
- const contentLength = Number(c.req.header("content-length") ?? 0);
66
- if (contentLength > 512_000) {
67
- return c.json({ error: "Payload too large" }, 413);
68
- }
69
- let argumentsArray;
70
- try {
71
- const body = await c.req.json();
72
- if (!Array.isArray(body))
73
- throw new Error("Expected array");
74
- argumentsArray = body;
75
- }
76
- catch {
77
- return c.json({ error: "Invalid request body" }, 400);
78
- }
79
- try {
80
- const result = await serverFunctionsRegistry.get(functionId)(...argumentsArray);
81
- return c.json(result);
82
- }
83
- catch (error) {
84
- const message = error instanceof Error ? error.message : "Internal server error";
85
- return c.json({ error: message }, 500);
86
- }
87
- });
88
- app.use(async (c, next) => {
89
- const p = c.req.path;
90
- if (isDev && p.includes(".hot-update.")) {
91
- const targetUrl = `${devServerUrl}${p}`;
92
- try {
93
- const response = await fetch(targetUrl);
94
- if (!response.ok)
95
- return c.text("hot update not found", 404);
96
- const contentType = response.headers.get("content-type");
97
- if (contentType)
98
- c.header("content-type", contentType);
99
- c.header("Cache-Control", "no-cache, no-store, must-revalidate");
100
- return c.body(await response.arrayBuffer());
101
- }
102
- catch {
103
- return c.text("failed to fetch hot update", 500);
104
- }
105
- }
106
- await next();
107
- });
108
- registerServerRoutes(app);
109
- let memoizedHtmlTemplate = "";
110
- let memoizedManifest = null;
111
- const templatePath = path.resolve(process.cwd(), "./dist/client/index.html");
112
- const manifestPath = path.resolve(process.cwd(), "./dist/route-manifest.json");
113
- const loadManifestAndTemplate = async () => {
114
- if (isDev) {
115
- try {
116
- memoizedHtmlTemplate = await fetch(`${devServerUrl}/index.html`).then((r) => {
117
- if (!r.ok)
118
- throw new Error(`index.html fetch failed: ${r.status}`);
119
- return r.text();
120
- });
121
- }
122
- catch (err) {
123
- console.error("[anaemia engine sync error - HTML fetch failed]:", err);
124
- memoizedHtmlTemplate = "";
125
- }
126
- try {
127
- if (fs.existsSync(manifestPath)) {
128
- memoizedManifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
129
- }
130
- else {
131
- memoizedManifest = { routes: [], chunks: {}, errors: {} };
132
- }
133
- }
134
- catch (err) {
135
- console.error("[anaemia engine sync error - manifest read failed]:", err);
136
- memoizedManifest = { routes: [], chunks: {}, errors: {} };
137
- }
138
- }
139
- else {
140
- try {
141
- if (fs.existsSync(templatePath))
142
- memoizedHtmlTemplate = fs.readFileSync(templatePath, "utf-8");
143
- if (fs.existsSync(manifestPath))
144
- memoizedManifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
145
- }
146
- catch {
147
- console.warn("build assets not fully initialized during bootstrapping cycle.");
148
- }
149
- }
150
- sortedRoutes = null;
151
- };
152
- const normalizeAssetUrl = (url) => {
153
- if (!url || typeof url !== "string")
154
- return "";
155
- if (url.startsWith("http://") || url.startsWith("https://"))
156
- return url;
157
- return url.startsWith("/") ? url : `/${url}`;
158
- };
159
- async function runGuards(pattern, ctx) {
160
- const chain = serverGuardRegistry.get(pattern) ?? [];
161
- for (const loadGuards of chain) {
162
- const guards = await loadGuards();
163
- for (const guard of guards) {
164
- const result = await guard(ctx);
165
- if (result && ("redirect" in result || "status" in result))
166
- return result;
167
- }
168
- }
169
- return null;
170
- }
171
- function matchRoute(manifest, reqPath) {
172
- if (!sortedRoutes) {
173
- sortedRoutes = [...manifest.routes].sort((a, b) => {
174
- const score = (pattern) => {
175
- const segments = pattern.split("/").filter(Boolean);
176
- return segments.reduce((acc, s) => {
177
- if (s.startsWith(":"))
178
- return acc - 1;
179
- if (s === "*" || s.startsWith("*"))
180
- return acc - 2;
181
- return acc;
182
- }, segments.length * 10);
183
- };
184
- return score(b.urlPattern) - score(a.urlPattern);
185
- });
186
- }
187
- for (const route of sortedRoutes) {
188
- const regexStr = route.urlPattern.replace(/:([a-zA-Z0-9_-]+)/g, "(?<$1>[^/]+)").replace(/\*([a-zA-Z0-9_-]*)/g, "(?<catchall>.*)");
189
- const match = new RegExp(`^${regexStr}$`).exec(reqPath);
190
- if (match) {
191
- return {
192
- activeChunk: route.chunkName,
193
- targetPattern: route.urlPattern,
194
- statusCode: 200,
195
- params: match.groups ? { ...match.groups } : {},
196
- };
197
- }
198
- }
199
- return {
200
- activeChunk: "route-404",
201
- targetPattern: manifest.errors?.["404"] || "",
202
- statusCode: 404,
203
- params: {},
204
- };
205
- }
206
- app.get("*", async (c) => {
207
- if (isDev)
208
- await loadManifestAndTemplate();
209
- let template = memoizedHtmlTemplate;
210
- let manifest = memoizedManifest;
211
- if (!template || !manifest) {
212
- return c.text("anaemia engine error: build assets are missing", 500);
213
- }
214
- const reqPath = c.req.path;
215
- const { activeChunk, targetPattern, statusCode: matchedStatus, params } = matchRoute(manifest, reqPath);
216
- let statusCode = matchedStatus;
217
- const loaderArgs = { params, request: c.req.raw };
218
- const store = ssrStorage.getStore() || new Map();
219
- let htmlPayload;
220
- if (targetPattern) {
221
- try {
222
- const guardResult = await runGuards(targetPattern, { params, request: c.req.raw, url: reqPath });
223
- if (guardResult) {
224
- if ("redirect" in guardResult)
225
- return c.redirect(guardResult.redirect, (guardResult.status ?? 302));
226
- if ("status" in guardResult)
227
- statusCode = guardResult.status;
228
- }
229
- }
230
- catch (err) {
231
- console.error("[anaemia] guard threw unexpectedly:", err);
232
- return c.text("Internal Server Error", 500);
233
- }
234
- }
235
- try {
236
- htmlPayload = await ssrStorage.run(store, async () => {
237
- if (targetPattern) {
238
- const executableLoader = serverLoaderRegistry.get(targetPattern);
239
- if (executableLoader) {
240
- const initialLoaderData = await executableLoader(loaderArgs);
241
- store.set("__LOADER_DATA__", initialLoaderData);
242
- }
243
- }
244
- await preloadActiveClientRoute(reqPath);
245
- return await renderToStringAsync(() => (<Router url={reqPath}>
246
- <App />
247
- </Router>));
248
- });
249
- }
250
- catch (err) {
251
- statusCode = 500;
252
- console.error("[anaemia framework] runtime execution crash handled:", err);
253
- const error500Pattern = manifest.errors?.["500"];
254
- const error500Loader = error500Pattern ? serverLoaderRegistry.get(error500Pattern) : null;
255
- if (error500Loader) {
256
- const message = err instanceof Error ? err.message : String(err);
257
- const stack = err instanceof Error ? err.stack : undefined;
258
- const runtimeContextPayload = { message, stack: isDev ? stack : undefined };
259
- store.set("__LOADER_DATA__", runtimeContextPayload);
260
- try {
261
- htmlPayload = await ssrStorage.run(store, async () => {
262
- return await renderToStringAsync(() => (<Router url={error500Pattern}>
263
- <App />
264
- </Router>));
265
- });
266
- }
267
- catch {
268
- htmlPayload = `<h1>500 Internal Server Error</h1>`;
269
- }
270
- }
271
- else {
272
- const stack = err instanceof Error ? err.stack : String(err);
273
- htmlPayload = `<h1>500 Internal Server Error</h1><pre>${isDev ? stack : ""}</pre>`;
274
- }
275
- }
276
- let assetScripts = "";
277
- let assetStyles = "";
278
- if (manifest.chunks) {
279
- const processChunkAssets = (chunk) => {
280
- if (!chunk)
281
- return;
282
- if (chunk.js) {
283
- const jsSpecs = Array.isArray(chunk.js) ? chunk.js : [chunk.js];
284
- jsSpecs.forEach((jsFile) => {
285
- assetScripts += `<script type="module" src="${normalizeAssetUrl(jsFile)}"></script>\n`;
286
- });
287
- }
288
- if (chunk.css) {
289
- const cssSpecs = Array.isArray(chunk.css) ? chunk.css : [chunk.css];
290
- cssSpecs.forEach((cssFile) => {
291
- assetStyles += `<link rel="stylesheet" href="${normalizeAssetUrl(cssFile)}">\n`;
292
- });
293
- }
294
- };
295
- processChunkAssets(manifest.chunks["client"]);
296
- if (manifest.chunks["commons"])
297
- processChunkAssets(manifest.chunks["commons"]);
298
- if (manifest.chunks["vendors"])
299
- processChunkAssets(manifest.chunks["vendors"]);
300
- if (activeChunk && activeChunk !== "client")
301
- processChunkAssets(manifest.chunks[activeChunk]);
302
- }
303
- const hydrationScript = generateHydrationScript();
304
- const rawStorePayload = Object.fromEntries(store);
305
- const finalHydrationStatePayload = {
306
- __LOADER_DATA__: rawStorePayload.__LOADER_DATA__ || {},
307
- __SERVER_FUNCTION_DATA__: rawStorePayload.__SERVER_FUNCTION_DATA__ || {},
308
- };
309
- const serializedData = JSON.stringify(finalHydrationStatePayload).replace(/&/g, "\\u0026").replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/\//g, "\\u002f");
310
- const dataScript = `<script id="__ANAEMIA_DATA__" type="application/json">${serializedData}</script>\n`;
311
- const devNoCacheTag = isDev ? `<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` : "";
312
- const combinedHeadInjections = `${devNoCacheTag}${assetStyles}${dataScript}${hydrationScript}`;
313
- const sanitizedPayload = htmlPayload.trim();
314
- let completeHtmlOutput = ENTRY_TAG_REGEX.test(template) ? template.replace(ENTRY_TAG_REGEX, (_, open, _tag, _inner, close) => `${open}${sanitizedPayload}${close}`) : template.replace("</body>", () => `<div anaemia-entry>${sanitizedPayload}</div></body>`);
315
- completeHtmlOutput = completeHtmlOutput.replace("<head>", `<head>${combinedHeadInjections}`);
316
- completeHtmlOutput = completeHtmlOutput.replace("</body>", `${assetScripts}</body>`);
317
- if (isDev) {
318
- c.header("Cache-Control", "no-cache, no-store, must-revalidate");
319
- c.header("Pragma", "no-cache");
320
- c.header("Expires", "0");
321
- }
322
- c.status(statusCode);
323
- return c.html(completeHtmlOutput);
324
- });
325
- loadManifestAndTemplate()
23
+ manifestStore
24
+ .load()
326
25
  .then(() => {
327
- serve({ fetch: app.fetch, port }, (info) => {
328
- console.log(`[anaemia framework] server live at http://localhost:${info.port}`);
329
- });
26
+ serveServer(app, env);
330
27
  })
331
28
  .catch((err) => {
332
29
  console.error("[anaemia] failed to initialize:", err);
@@ -1 +1 @@
1
- {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../src/runtime/resources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAcrF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EACjD,MAAM,EAAE,MAAM,MAAM,EACpB,QAAQ,EAAE,CAAC,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG;IACpD,kBAAkB,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACvD,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,EACD,OAAO,CAAC,EAAE,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,GACxC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAuCjC"}
1
+ {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../src/runtime/resources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAerF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EACjD,MAAM,EAAE,MAAM,MAAM,EACpB,QAAQ,EAAE,CAAC,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG;IACpD,kBAAkB,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACvD,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,EACD,OAAO,CAAC,EAAE,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,GACxC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAuCjC"}
@@ -1,11 +1,12 @@
1
1
  import { createResource } from "solid-js";
2
2
  import { isServer } from "solid-js/web";
3
+ import { SERVER_FUNCTION_DATA_KEY } from "./shared/constants.js";
3
4
  export function createServerResource(source, serverFn, options) {
4
5
  if (isServer) {
5
6
  let ssrInitialValue = undefined;
6
7
  const store = globalThis.__ANAEMIA_SERVER_STORAGE__?.getStore?.();
7
8
  if (store && serverFn.id) {
8
- const fnData = store.get("__SERVER_FUNCTION_DATA__");
9
+ const fnData = store.get(SERVER_FUNCTION_DATA_KEY);
9
10
  const fnCache = fnData?.[serverFn.id];
10
11
  if (fnCache) {
11
12
  const sourceValue = source();
@@ -1 +1 @@
1
- {"version":3,"file":"route-data.d.ts","sourceRoot":"","sources":["../../src/runtime/route-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8D,KAAK,GAAG,EAAE,MAAM,UAAU,CAAC;AAEhG,OAAO,EAA0B,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAIhD,KAAK,UAAU,CAAC,OAAO,SAAS,MAAM,IAAI;IACxC,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,KAAK,wBAAwB,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM,IAAI;IAC/D,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClE,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CACvB,CAAC;AA6BF,wBAAgB,mBAAmB,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,wBAAwB,CAAC,OAAO,CAAC,eA2B5G;AAED,wBAAgB,YAAY,CAAC,CAAC,GAAG,OAAO,KAAK,MAAM,CAAC,CAMnD"}
1
+ {"version":3,"file":"route-data.d.ts","sourceRoot":"","sources":["../../src/runtime/route-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8D,KAAK,GAAG,EAAE,MAAM,UAAU,CAAC;AAEhG,OAAO,EAA0B,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAKhD,KAAK,UAAU,CAAC,OAAO,SAAS,MAAM,IAAI;IACxC,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,KAAK,wBAAwB,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM,IAAI;IAC/D,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClE,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CACvB,CAAC;AA6BF,wBAAgB,mBAAmB,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,wBAAwB,CAAC,OAAO,CAAC,eA2B5G;AAED,wBAAgB,YAAY,CAAC,CAAC,GAAG,OAAO,KAAK,MAAM,CAAC,CAMnD"}
@@ -3,20 +3,21 @@ import { isServer } from "solid-js/web";
3
3
  import { useParams, useLocation } from "@solidjs/router";
4
4
  import { ssrStorage } from "./context.js";
5
5
  import { createRouteRequest } from "./route-request.js";
6
+ import { ANAEMIA_DATA_SCRIPT_ID, LOADER_DATA_KEY } from "./shared/constants.js";
6
7
  const RouteDataContext = createContext();
7
8
  let hasReadClientHydrationData = false;
8
9
  function readSSRData() {
9
10
  if (isServer) {
10
- return ssrStorage.getStore()?.get("__LOADER_DATA__");
11
+ return ssrStorage.getStore()?.get(LOADER_DATA_KEY);
11
12
  }
12
13
  if (hasReadClientHydrationData)
13
14
  return undefined;
14
15
  hasReadClientHydrationData = true;
15
- const el = document.getElementById("__ANAEMIA_DATA__");
16
+ const el = document.getElementById(ANAEMIA_DATA_SCRIPT_ID);
16
17
  if (!el?.textContent)
17
18
  return undefined;
18
19
  try {
19
- return JSON.parse(el.textContent).__LOADER_DATA__;
20
+ return JSON.parse(el.textContent)[LOADER_DATA_KEY];
20
21
  }
21
22
  catch {
22
23
  return undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"route-request.d.ts","sourceRoot":"","sources":["../../src/runtime/route-request.ts"],"names":[],"mappings":"AAIA,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAQ5D"}
1
+ {"version":3,"file":"route-request.d.ts","sourceRoot":"","sources":["../../src/runtime/route-request.ts"],"names":[],"mappings":"AAKA,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAQ5D"}
@@ -1,9 +1,10 @@
1
1
  import { isServer } from "solid-js/web";
2
2
  import { ssrStorage } from "./context.js";
3
+ import { HONO_CONTEXT_KEY } from "./shared/constants.js";
3
4
  export function createRouteRequest(pathname) {
4
5
  if (isServer) {
5
- const honoContext = ssrStorage.getStore()?.get("honoContext");
6
- const request = honoContext?.req?.raw;
6
+ const honoContext = ssrStorage.getStore()?.get(HONO_CONTEXT_KEY);
7
+ const request = honoContext?.req.raw;
7
8
  if (request)
8
9
  return request;
9
10
  return new Request(new URL(pathname, "http://localhost").toString());
@@ -1 +1 @@
1
- {"version":3,"file":"rpc-client.d.ts","sourceRoot":"","sources":["../../src/runtime/rpc-client.ts"],"names":[],"mappings":"AAqDA,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM;cACD,OAAO,EAAE;;gCAgCF,OAAO,EAAE;EAoB/D"}
1
+ {"version":3,"file":"rpc-client.d.ts","sourceRoot":"","sources":["../../src/runtime/rpc-client.ts"],"names":[],"mappings":"AAsDA,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM;cACD,OAAO,EAAE;;gCAgCF,OAAO,EAAE;EAoB/D"}