@anaemia/core 0.3.7 → 0.4.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.
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;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,CAyB1F"}
@@ -3,7 +3,7 @@ export const serverFunctionsRegistry = new Map();
3
3
  export const ssrStorage = new AsyncLocalStorage();
4
4
  globalThis.__ANAEMIA_SERVER_STORAGE__ = ssrStorage;
5
5
  export function runOnServer(backendFn, id) {
6
- const hashId = id || "";
6
+ const hashId = id ?? crypto.randomUUID();
7
7
  serverFunctionsRegistry.set(hashId, backendFn);
8
8
  const rpcProxy = async function (...args) {
9
9
  const result = await backendFn(...args);
@@ -9,11 +9,9 @@ if (!mountTarget) {
9
9
  const root = mountTarget;
10
10
  async function start() {
11
11
  await preloadActiveClientRoute(window.location.pathname);
12
- const mount = root.hasChildNodes()
13
- ? hydrate
14
- : render;
12
+ const mount = root.hasChildNodes() ? hydrate : render;
15
13
  mount(() => (<Router>
16
14
  <App />
17
15
  </Router>), root);
18
16
  }
19
- start();
17
+ await start();
@@ -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":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AA2C5B,QAAA,MAAM,GAAG,4EAAa,CAAC;AAqXvB,eAAe,GAAG,CAAC"}
@@ -6,7 +6,7 @@ import { renderToStringAsync, generateHydrationScript } from "solid-js/web";
6
6
  import { Router } from "@solidjs/router";
7
7
  import { ssrStorage, serverFunctionsRegistry } from "./context.js";
8
8
  import fs from "node:fs";
9
- import path from "path";
9
+ import path from "node:path";
10
10
  // @ts-expect-error - resolved by Rspack
11
11
  import App from "anaemia-user-app";
12
12
  // @ts-expect-error - resolved by Rspack
@@ -185,7 +185,9 @@ function matchRoute(manifest, reqPath) {
185
185
  });
186
186
  }
187
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>.*)");
188
+ const regexStr = route.urlPattern
189
+ .replace(/:([a-zA-Z0-9_-]+)/g, "(?<$1>[^/]+)")
190
+ .replace(/\*([a-zA-Z0-9_-]*)/g, "(?<catchall>.*)");
189
191
  const match = new RegExp(`^${regexStr}$`).exec(reqPath);
190
192
  if (match) {
191
193
  return {
@@ -206,8 +208,8 @@ function matchRoute(manifest, reqPath) {
206
208
  app.get("*", async (c) => {
207
209
  if (isDev)
208
210
  await loadManifestAndTemplate();
209
- let template = memoizedHtmlTemplate;
210
- let manifest = memoizedManifest;
211
+ const template = memoizedHtmlTemplate;
212
+ const manifest = memoizedManifest;
211
213
  if (!template || !manifest) {
212
214
  return c.text("anaemia engine error: build assets are missing", 500);
213
215
  }
@@ -219,7 +221,11 @@ app.get("*", async (c) => {
219
221
  let htmlPayload;
220
222
  if (targetPattern) {
221
223
  try {
222
- const guardResult = await runGuards(targetPattern, { params, request: c.req.raw, url: reqPath });
224
+ const guardResult = await runGuards(targetPattern, {
225
+ params,
226
+ request: c.req.raw,
227
+ url: reqPath,
228
+ });
223
229
  if (guardResult) {
224
230
  if ("redirect" in guardResult)
225
231
  return c.redirect(guardResult.redirect, (guardResult.status ?? 302));
@@ -275,43 +281,47 @@ app.get("*", async (c) => {
275
281
  }
276
282
  let assetScripts = "";
277
283
  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
- });
284
+ const processChunkAssets = (chunk) => {
285
+ if (!chunk)
286
+ return;
287
+ if (chunk.js) {
288
+ const jsSpecs = Array.isArray(chunk.js) ? chunk.js : [chunk.js];
289
+ for (const jsFile of jsSpecs) {
290
+ assetScripts += `<script type="module" src="${normalizeAssetUrl(jsFile)}"></script>\n`;
287
291
  }
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
- });
292
+ }
293
+ if (chunk.css) {
294
+ const cssSpecs = Array.isArray(chunk.css) ? chunk.css : [chunk.css];
295
+ for (const cssFile of cssSpecs) {
296
+ assetStyles += `<link rel="stylesheet" href="${normalizeAssetUrl(cssFile)}">\n`;
293
297
  }
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
- }
298
+ }
299
+ };
300
+ processChunkAssets(manifest.chunks["client"]);
301
+ processChunkAssets(manifest.chunks["commons"]);
302
+ processChunkAssets(manifest.chunks["vendors"]);
303
+ if (activeChunk && activeChunk !== "client")
304
+ processChunkAssets(manifest.chunks[activeChunk]);
303
305
  const hydrationScript = generateHydrationScript();
304
306
  const rawStorePayload = Object.fromEntries(store);
305
307
  const finalHydrationStatePayload = {
306
308
  __LOADER_DATA__: rawStorePayload.__LOADER_DATA__ || {},
307
309
  __SERVER_FUNCTION_DATA__: rawStorePayload.__SERVER_FUNCTION_DATA__ || {},
308
310
  };
309
- const serializedData = JSON.stringify(finalHydrationStatePayload).replace(/&/g, "\\u0026").replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/\//g, "\\u002f");
311
+ const serializedData = JSON.stringify(finalHydrationStatePayload)
312
+ .replace(/&/g, "\\u0026")
313
+ .replace(/</g, "\\u003c")
314
+ .replace(/>/g, "\\u003e")
315
+ .replace(/\//g, "\\u002f");
310
316
  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` : "";
317
+ const devNoCacheTag = isDev
318
+ ? `<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`
319
+ : "";
312
320
  const combinedHeadInjections = `${devNoCacheTag}${assetStyles}${dataScript}${hydrationScript}`;
313
321
  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>`);
322
+ let completeHtmlOutput = ENTRY_TAG_REGEX.test(template)
323
+ ? template.replace(ENTRY_TAG_REGEX, (_, open, _tag, _inner, close) => `${open}${sanitizedPayload}${close}`)
324
+ : template.replace("</body>", () => `<div anaemia-entry>${sanitizedPayload}</div></body>`);
315
325
  completeHtmlOutput = completeHtmlOutput.replace("<head>", `<head>${combinedHeadInjections}`);
316
326
  completeHtmlOutput = completeHtmlOutput.replace("</body>", `${assetScripts}</body>`);
317
327
  if (isDev) {
@@ -3,7 +3,7 @@ import { ssrStorage } from "./context.js";
3
3
  export function createRouteRequest(pathname) {
4
4
  if (isServer) {
5
5
  const honoContext = ssrStorage.getStore()?.get("honoContext");
6
- const request = honoContext?.req?.raw;
6
+ const request = honoContext?.req.raw;
7
7
  if (request)
8
8
  return request;
9
9
  return new Request(new URL(pathname, "http://localhost").toString());
@@ -37,7 +37,7 @@ export function $$executeClientRpc(hashId) {
37
37
  return match.data;
38
38
  }
39
39
  }
40
- return undefined;
40
+ return;
41
41
  }
42
42
  ensureCacheInitialized();
43
43
  const serverFunctionData = _clientCache?.__SERVER_FUNCTION_DATA__?.[hashId];
@@ -54,7 +54,7 @@ export function $$executeClientRpc(hashId) {
54
54
  });
55
55
  if (!response.ok)
56
56
  throw new Error(`[anaemia] RPC execution failed: ${response.status}`);
57
- return await response.json();
57
+ return (await response.json());
58
58
  };
59
59
  asyncRpcCall.id = hashId;
60
60
  asyncRpcCall.readHydrationCache = function (...args) {
@@ -68,7 +68,7 @@ export function $$executeClientRpc(hashId) {
68
68
  return match.data;
69
69
  }
70
70
  }
71
- return undefined;
71
+ return;
72
72
  }
73
73
  ensureCacheInitialized();
74
74
  const serverFunctionData = _clientCache?.__SERVER_FUNCTION_DATA__?.[hashId];
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,UAAU,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IACxF,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,CAAC,YAAY,GAAG,OAAO,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY,CAAC;AAExL;;;GAGG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAEhG,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAErJ,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,GAAG,SAAS,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,kBAAkB,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,GAAG,SAAS,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,kBAAkB,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE/T,MAAM,MAAM,cAAc,CAAC,IAAI,SAAS,OAAO,EAAE,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG;IAClG,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,UAAU,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IACxF,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,CAAC,YAAY,GAAG,OAAO,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CACnH,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,KACrB,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY,CAAC;AAE1C;;;GAGG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAEhG,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,WAAW,GACnB,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,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD,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,kBAAkB,CAAA;CAAE,GACjD;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,kBAAkB,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEtH,MAAM,MAAM,cAAc,CAAC,IAAI,SAAS,OAAO,EAAE,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG;IAClG,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anaemia/core",
3
- "version": "0.3.7",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -28,6 +28,7 @@
28
28
  "types": "./dist/plugins/index.d.ts",
29
29
  "import": "./dist/plugins/index.js"
30
30
  },
31
+ "./client": "./anaemia.d.ts",
31
32
  "./package.json": "./package.json"
32
33
  },
33
34
  "dependencies": {
@@ -36,6 +37,12 @@
36
37
  "@solidjs/router": "^0.16.1",
37
38
  "hono": "^4.12.23"
38
39
  },
40
+ "devDependencies": {
41
+ "@types/babel__core": "^7.20.5",
42
+ "@types/node": "^25.9.1",
43
+ "jiti": "^2.7.0",
44
+ "playwright": "^1.60.0"
45
+ },
39
46
  "peerDependencies": {
40
47
  "@rspack/core": "*",
41
48
  "lightningcss": "^1.32.0",
@@ -49,12 +56,6 @@
49
56
  "optional": true
50
57
  }
51
58
  },
52
- "devDependencies": {
53
- "@types/babel__core": "^7.20.5",
54
- "@types/node": "^25.9.1",
55
- "jiti": "^2.7.0",
56
- "playwright": "^1.60.0"
57
- },
58
59
  "scripts": {
59
60
  "build": "tsc",
60
61
  "test": "pnpm run build && node --test test/*.test.mjs",
@@ -0,0 +1,75 @@
1
+ /// <reference types="@rspack/core/module" />
2
+
3
+ // asset imports
4
+ declare module "*.png" {
5
+ const src: string;
6
+ export default src;
7
+ }
8
+ declare module "*.jpg" {
9
+ const src: string;
10
+ export default src;
11
+ }
12
+ declare module "*.jpeg" {
13
+ const src: string;
14
+ export default src;
15
+ }
16
+ declare module "*.gif" {
17
+ const src: string;
18
+ export default src;
19
+ }
20
+ declare module "*.webp" {
21
+ const src: string;
22
+ export default src;
23
+ }
24
+ declare module "*.avif" {
25
+ const src: string;
26
+ export default src;
27
+ }
28
+ declare module "*.ico" {
29
+ const src: string;
30
+ export default src;
31
+ }
32
+ declare module "*.svg" {
33
+ const src: string;
34
+ export default src;
35
+ }
36
+
37
+ // query suffixes
38
+ declare module "*?raw" {
39
+ const content: string;
40
+ export default content;
41
+ }
42
+ declare module "*?url" {
43
+ const src: string;
44
+ export default src;
45
+ }
46
+ declare module "*?inline" {
47
+ const src: string;
48
+ export default src;
49
+ }
50
+
51
+ // CSS modules
52
+ declare module "*.module.css" {
53
+ const classes: Record<string, string>;
54
+ export default classes;
55
+ }
56
+ declare module "*.module.scss" {
57
+ const classes: Record<string, string>;
58
+ export default classes;
59
+ }
60
+ declare module "*.module.sass" {
61
+ const classes: Record<string, string>;
62
+ export default classes;
63
+ }
64
+
65
+ // import.meta.env
66
+ interface ImportMetaEnv {
67
+ readonly MODE: string;
68
+ readonly DEV: boolean;
69
+ readonly PROD: boolean;
70
+ readonly [key: string]: string | boolean | undefined;
71
+ }
72
+
73
+ interface ImportMeta {
74
+ readonly env: ImportMetaEnv;
75
+ }
package/src/config.ts CHANGED
@@ -45,6 +45,14 @@ export interface AnaemiaConfig {
45
45
  styles?: {
46
46
  sass?: boolean;
47
47
  modules?: boolean;
48
+
49
+ /**
50
+ * customize the generated CSS module class names.
51
+ * defaults to "[name]__[local]__[hash:base64:5]" in both development and production
52
+ * for readable, themeable class names. Set to "[hash:base64:8]" if you prefer
53
+ * fully hashed production output and don't need external theme support.
54
+ */
55
+ modulesLocalIdentName?: string;
48
56
  };
49
57
  experimental?: {
50
58
  outputModule?: boolean;
@@ -63,7 +71,7 @@ export interface AnaemiaConfig {
63
71
  * client: { __APP_VERSION__: JSON.stringify("1.0.0") },
64
72
  * server: { __DB_POOL_SIZE__: "10" }
65
73
  * }
66
- */
74
+ */
67
75
  define?: {
68
76
  client?: Record<string, string>;
69
77
  server?: Record<string, string>;
package/src/index.ts CHANGED
@@ -5,4 +5,12 @@ export { RouteDataController, useRouteData } from "./runtime/route-data.js";
5
5
  export { $$executeClientRpc } from "./runtime/rpc-client.js";
6
6
  export { createServerResource } from "./runtime/resources.js";
7
7
 
8
- export type { LoaderArgs, LoaderFunction, InferServerData, GuardContext, GuardResult, GuardFn, ServerFunction } from "./types.js";
8
+ export type {
9
+ LoaderArgs,
10
+ LoaderFunction,
11
+ InferServerData,
12
+ GuardContext,
13
+ GuardResult,
14
+ GuardFn,
15
+ ServerFunction,
16
+ } from "./types.js";
@@ -1 +1 @@
1
- export { anaemiaLightningCssPlugin } from "./lightningcss.js";
1
+ export { anaemiaLightningCssPlugin } from "./lightningcss.js";
@@ -11,12 +11,12 @@ export function anaemiaLightningCssPlugin(options: { browserslist?: string[] } =
11
11
 
12
12
  const localRequire = createRequire(import.meta.url);
13
13
  let rspackModule: RspackModule;
14
-
14
+
15
15
  try {
16
16
  rspackModule = localRequire("@rspack/core");
17
17
  } catch {
18
18
  throw new Error(
19
- "[anaemia] The LightningCSS plugin requires '@rspack/core' to be available in the execution workspace."
19
+ "[anaemia] The LightningCSS plugin requires '@rspack/core' to be available in the execution workspace.",
20
20
  );
21
21
  }
22
22
 
@@ -27,22 +27,22 @@ export function anaemiaLightningCssPlugin(options: { browserslist?: string[] } =
27
27
  const isProd = process.env.NODE_ENV === "production";
28
28
 
29
29
  if (config.module?.rules) {
30
- config.module.rules.forEach((rule) => {
30
+ for (const rule of config.module.rules) {
31
31
  if (rule && typeof rule === "object" && rule.test && rule.test.toString().includes("ss")) {
32
32
  const currentUse = Array.isArray(rule.use) ? rule.use : [];
33
-
33
+
34
34
  rule.use = [
35
35
  ...currentUse,
36
36
  {
37
37
  loader: "builtin:lightningcss-loader",
38
- options: {
38
+ options: {
39
39
  targets,
40
40
  modules: rule.type === "css/auto",
41
41
  },
42
42
  },
43
43
  ];
44
44
  }
45
- });
45
+ }
46
46
  }
47
47
 
48
48
  if (isProd) {
@@ -63,24 +63,24 @@ export function anaemiaLightningCssPlugin(options: { browserslist?: string[] } =
63
63
 
64
64
  serverRspackConfig(config) {
65
65
  if (config.module?.rules) {
66
- config.module.rules.forEach((rule) => {
66
+ for (const rule of config.module.rules) {
67
67
  if (rule && typeof rule === "object" && rule.test && rule.test.toString().includes("ss")) {
68
68
  const currentUse = Array.isArray(rule.use) ? rule.use : [];
69
-
69
+
70
70
  rule.use = [
71
71
  ...currentUse,
72
72
  {
73
73
  loader: "builtin:lightningcss-loader",
74
- options: {
74
+ options: {
75
75
  targets,
76
76
  modules: rule.type === "css/auto",
77
77
  },
78
78
  },
79
79
  ];
80
80
  }
81
- });
81
+ }
82
82
  }
83
83
  return config;
84
84
  },
85
85
  };
86
- }
86
+ }
@@ -3,4 +3,4 @@ export const ssrStorage = null as any;
3
3
  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
4
4
  export const serverFunctionsRegistry = new Map<string, Function>();
5
5
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
- export const runOnServer = null as any;
6
+ export const runOnServer = null as any;
@@ -7,7 +7,7 @@ export const ssrStorage = new AsyncLocalStorage<Map<string, unknown>>();
7
7
  (globalThis as unknown as Record<string, unknown>).__ANAEMIA_SERVER_STORAGE__ = ssrStorage;
8
8
 
9
9
  export function runOnServer<T extends AnyFn>(backendFn: T, id?: string): T & { id: string } {
10
- const hashId = id || "";
10
+ const hashId = id ?? crypto.randomUUID();
11
11
  serverFunctionsRegistry.set(hashId, backendFn);
12
12
 
13
13
  const rpcProxy = async function (...args: unknown[]) {
@@ -17,7 +17,10 @@ export function runOnServer<T extends AnyFn>(backendFn: T, id?: string): T & { i
17
17
  if (!store.has("__SERVER_FUNCTION_DATA__")) {
18
18
  store.set("__SERVER_FUNCTION_DATA__", {});
19
19
  }
20
- const functionCache = store.get("__SERVER_FUNCTION_DATA__") as Record<string, Record<string, unknown>>;
20
+ const functionCache = store.get("__SERVER_FUNCTION_DATA__") as Record<
21
+ string,
22
+ Record<string, unknown> | undefined
23
+ >;
21
24
  if (!functionCache[hashId]) {
22
25
  functionCache[hashId] = {};
23
26
  }
@@ -28,4 +31,4 @@ export function runOnServer<T extends AnyFn>(backendFn: T, id?: string): T & { i
28
31
  };
29
32
  rpcProxy.id = hashId;
30
33
  return rpcProxy as unknown as T & { id: string };
31
- }
34
+ }
@@ -4,9 +4,7 @@ import { Router } from "@solidjs/router";
4
4
  // @ts-expect-error - resolved by Rspack
5
5
  import App, { preloadActiveClientRoute } from "anaemia-user-app";
6
6
 
7
- const mountTarget = document.querySelector(
8
- "[anaemia-entry]"
9
- ) as HTMLElement | null;
7
+ const mountTarget = document.querySelector("[anaemia-entry]") as HTMLElement | null;
10
8
 
11
9
  if (!mountTarget) {
12
10
  throw new Error("[anaemia] missing mount target");
@@ -17,9 +15,7 @@ const root = mountTarget;
17
15
  async function start() {
18
16
  await preloadActiveClientRoute(window.location.pathname);
19
17
 
20
- const mount = root.hasChildNodes()
21
- ? hydrate
22
- : render;
18
+ const mount = root.hasChildNodes() ? hydrate : render;
23
19
 
24
20
  mount(
25
21
  () => (
@@ -27,8 +23,8 @@ async function start() {
27
23
  <App />
28
24
  </Router>
29
25
  ),
30
- root
26
+ root,
31
27
  );
32
28
  }
33
29
 
34
- start();
30
+ await start();
@@ -7,7 +7,7 @@ import { renderToStringAsync, generateHydrationScript } from "solid-js/web";
7
7
  import { Router } from "@solidjs/router";
8
8
  import { ssrStorage, serverFunctionsRegistry } from "./context.js";
9
9
  import fs from "node:fs";
10
- import path from "path";
10
+ import path from "node:path";
11
11
  import type { StatusCode, RedirectStatusCode } from "hono/utils/http-status";
12
12
 
13
13
  // @ts-expect-error - resolved by Rspack
@@ -82,7 +82,7 @@ if (isDev) {
82
82
  "/assets/*",
83
83
  serveStatic({
84
84
  root: path.resolve(process.cwd(), "./dist/client"),
85
- })
85
+ }),
86
86
  );
87
87
  }
88
88
 
@@ -191,7 +191,16 @@ type RouteMatch = {
191
191
  params: Record<string, string>;
192
192
  };
193
193
 
194
- type GuardFn = (ctx: { params: Record<string, string>; request: Request; url: string }) => void | undefined | { redirect: string; status?: 301 | 302 | 307 | 308 } | { status: number; body?: string } | Promise<void | undefined | { redirect: string; status?: number } | { status: number; body?: string }>;
194
+ type GuardFn = (ctx: {
195
+ params: Record<string, string>;
196
+ request: Request;
197
+ url: string;
198
+ }) =>
199
+ | void
200
+ | undefined
201
+ | { redirect: string; status?: 301 | 302 | 307 | 308 }
202
+ | { status: number; body?: string }
203
+ | Promise<void | undefined | { redirect: string; status?: number } | { status: number; body?: string }>;
195
204
 
196
205
  async function runGuards(pattern: string, ctx: { params: Record<string, string>; request: Request; url: string }) {
197
206
  const chain: (() => Promise<GuardFn[]>)[] = serverGuardRegistry.get(pattern) ?? [];
@@ -221,7 +230,9 @@ function matchRoute(manifest: RouteManifest, reqPath: string): RouteMatch {
221
230
  }
222
231
 
223
232
  for (const route of sortedRoutes) {
224
- const regexStr = route.urlPattern.replace(/:([a-zA-Z0-9_-]+)/g, "(?<$1>[^/]+)").replace(/\*([a-zA-Z0-9_-]*)/g, "(?<catchall>.*)");
233
+ const regexStr = route.urlPattern
234
+ .replace(/:([a-zA-Z0-9_-]+)/g, "(?<$1>[^/]+)")
235
+ .replace(/\*([a-zA-Z0-9_-]*)/g, "(?<catchall>.*)");
225
236
 
226
237
  const match = new RegExp(`^${regexStr}$`).exec(reqPath);
227
238
  if (match) {
@@ -245,8 +256,8 @@ function matchRoute(manifest: RouteManifest, reqPath: string): RouteMatch {
245
256
  app.get("*", async (c) => {
246
257
  if (isDev) await loadManifestAndTemplate();
247
258
 
248
- let template = memoizedHtmlTemplate;
249
- let manifest = memoizedManifest;
259
+ const template = memoizedHtmlTemplate;
260
+ const manifest = memoizedManifest;
250
261
 
251
262
  if (!template || !manifest) {
252
263
  return c.text("anaemia engine error: build assets are missing", 500);
@@ -262,9 +273,14 @@ app.get("*", async (c) => {
262
273
 
263
274
  if (targetPattern) {
264
275
  try {
265
- const guardResult = await runGuards(targetPattern, { params, request: c.req.raw, url: reqPath });
276
+ const guardResult = await runGuards(targetPattern, {
277
+ params,
278
+ request: c.req.raw,
279
+ url: reqPath,
280
+ });
266
281
  if (guardResult) {
267
- if ("redirect" in guardResult) return c.redirect(guardResult.redirect, (guardResult.status ?? 302) as RedirectStatusCode);
282
+ if ("redirect" in guardResult)
283
+ return c.redirect(guardResult.redirect, (guardResult.status ?? 302) as RedirectStatusCode);
268
284
  if ("status" in guardResult) statusCode = guardResult.status as StatusCode;
269
285
  }
270
286
  } catch (err) {
@@ -324,28 +340,28 @@ app.get("*", async (c) => {
324
340
  let assetScripts = "";
325
341
  let assetStyles = "";
326
342
 
327
- if (manifest.chunks) {
328
- const processChunkAssets = (chunk: ChunkAssets | undefined) => {
329
- if (!chunk) return;
330
- if (chunk.js) {
331
- const jsSpecs = Array.isArray(chunk.js) ? chunk.js : [chunk.js];
332
- jsSpecs.forEach((jsFile: string) => {
333
- assetScripts += `<script type="module" src="${normalizeAssetUrl(jsFile)}"></script>\n`;
334
- });
343
+ const processChunkAssets = (chunk: ChunkAssets | undefined) => {
344
+ if (!chunk) return;
345
+ if (chunk.js) {
346
+ const jsSpecs = Array.isArray(chunk.js) ? chunk.js : [chunk.js];
347
+
348
+ for (const jsFile of jsSpecs) {
349
+ assetScripts += `<script type="module" src="${normalizeAssetUrl(jsFile)}"></script>\n`;
335
350
  }
336
- if (chunk.css) {
337
- const cssSpecs = Array.isArray(chunk.css) ? chunk.css : [chunk.css];
338
- cssSpecs.forEach((cssFile: string) => {
339
- assetStyles += `<link rel="stylesheet" href="${normalizeAssetUrl(cssFile)}">\n`;
340
- });
351
+ }
352
+ if (chunk.css) {
353
+ const cssSpecs = Array.isArray(chunk.css) ? chunk.css : [chunk.css];
354
+
355
+ for (const cssFile of cssSpecs) {
356
+ assetStyles += `<link rel="stylesheet" href="${normalizeAssetUrl(cssFile)}">\n`;
341
357
  }
342
- };
358
+ }
359
+ };
343
360
 
344
- processChunkAssets(manifest.chunks["client"]);
345
- if (manifest.chunks["commons"]) processChunkAssets(manifest.chunks["commons"]);
346
- if (manifest.chunks["vendors"]) processChunkAssets(manifest.chunks["vendors"]);
347
- if (activeChunk && activeChunk !== "client") processChunkAssets(manifest.chunks[activeChunk]);
348
- }
361
+ processChunkAssets(manifest.chunks["client"]);
362
+ processChunkAssets(manifest.chunks["commons"]);
363
+ processChunkAssets(manifest.chunks["vendors"]);
364
+ if (activeChunk && activeChunk !== "client") processChunkAssets(manifest.chunks[activeChunk]);
349
365
 
350
366
  const hydrationScript = generateHydrationScript();
351
367
  const rawStorePayload = Object.fromEntries(store);
@@ -355,16 +371,24 @@ app.get("*", async (c) => {
355
371
  __SERVER_FUNCTION_DATA__: rawStorePayload.__SERVER_FUNCTION_DATA__ || {},
356
372
  };
357
373
 
358
- const serializedData = JSON.stringify(finalHydrationStatePayload).replace(/&/g, "\\u0026").replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/\//g, "\\u002f");
374
+ const serializedData = JSON.stringify(finalHydrationStatePayload)
375
+ .replace(/&/g, "\\u0026")
376
+ .replace(/</g, "\\u003c")
377
+ .replace(/>/g, "\\u003e")
378
+ .replace(/\//g, "\\u002f");
359
379
 
360
380
  const dataScript = `<script id="__ANAEMIA_DATA__" type="application/json">${serializedData}</script>\n`;
361
381
 
362
- 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` : "";
382
+ const devNoCacheTag = isDev
383
+ ? `<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`
384
+ : "";
363
385
 
364
386
  const combinedHeadInjections = `${devNoCacheTag}${assetStyles}${dataScript}${hydrationScript}`;
365
387
  const sanitizedPayload = htmlPayload.trim();
366
388
 
367
- 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>`);
389
+ let completeHtmlOutput = ENTRY_TAG_REGEX.test(template)
390
+ ? template.replace(ENTRY_TAG_REGEX, (_, open, _tag, _inner, close) => `${open}${sanitizedPayload}${close}`)
391
+ : template.replace("</body>", () => `<div anaemia-entry>${sanitizedPayload}</div></body>`);
368
392
 
369
393
  completeHtmlOutput = completeHtmlOutput.replace("<head>", `<head>${combinedHeadInjections}`);
370
394
  completeHtmlOutput = completeHtmlOutput.replace("</body>", `${assetScripts}</body>`);
@@ -18,7 +18,7 @@ export function createServerResource<Source, Return>(
18
18
  readHydrationCache?: (s: Source) => Return | undefined;
19
19
  id?: string;
20
20
  },
21
- options?: ResourceOptions<Return, Source>
21
+ options?: ResourceOptions<Return, Source>,
22
22
  ): ResourceReturn<Return, unknown> {
23
23
  if (isServer) {
24
24
  let ssrInitialValue: Return | undefined = undefined;
@@ -61,7 +61,7 @@ export function RouteDataController<TParams extends Params = Params>(props: Rout
61
61
  {
62
62
  initialValue: ssrData,
63
63
  ssrLoadFrom: "initial",
64
- }
64
+ },
65
65
  );
66
66
 
67
67
  return createComponent(RouteDataContext.Provider, {
@@ -5,9 +5,9 @@ import { ssrStorage } from "./context.js";
5
5
  export function createRouteRequest(pathname: string): Request {
6
6
  if (isServer) {
7
7
  const honoContext = ssrStorage.getStore()?.get("honoContext") as Context | undefined;
8
- const request = honoContext?.req?.raw;
8
+ const request = honoContext?.req.raw;
9
9
  if (request) return request;
10
10
  return new Request(new URL(pathname, "http://localhost").toString());
11
11
  }
12
12
  return new Request(new URL(pathname, window.location.origin).toString());
13
- }
13
+ }
@@ -34,8 +34,8 @@ function ensureCacheInitialized() {
34
34
  }
35
35
 
36
36
  function findLooseCacheMatch(
37
- serverFunctionData: Record<string, unknown>,
38
- targetArg: string
37
+ serverFunctionData: Record<string, unknown> | null | undefined,
38
+ targetArg: string,
39
39
  ): CacheMatch | undefined {
40
40
  if (!serverFunctionData) return undefined;
41
41
  const strictKey = JSON.stringify([targetArg]);
@@ -62,7 +62,7 @@ export function $$executeClientRpc(hashId: string) {
62
62
  if (match) return match.data;
63
63
  }
64
64
  }
65
- return undefined;
65
+ return;
66
66
  }
67
67
 
68
68
  ensureCacheInitialized();
@@ -80,7 +80,7 @@ export function $$executeClientRpc(hashId: string) {
80
80
  body: JSON.stringify(args),
81
81
  });
82
82
  if (!response.ok) throw new Error(`[anaemia] RPC execution failed: ${response.status}`);
83
- return await response.json() as unknown;
83
+ return (await response.json()) as unknown;
84
84
  };
85
85
 
86
86
  asyncRpcCall.id = hashId;
@@ -94,7 +94,7 @@ export function $$executeClientRpc(hashId: string) {
94
94
  if (match) return match.data;
95
95
  }
96
96
  }
97
- return undefined;
97
+ return;
98
98
  }
99
99
 
100
100
  ensureCacheInitialized();
@@ -104,4 +104,4 @@ export function $$executeClientRpc(hashId: string) {
104
104
  };
105
105
 
106
106
  return asyncRpcCall;
107
- }
107
+ }
@@ -3,4 +3,4 @@ interface ImportMeta {
3
3
  accept(dep: string | string[], callback?: () => void): void;
4
4
  dispose(callback: (data: unknown) => void): void;
5
5
  };
6
- }
6
+ }
package/src/types.ts CHANGED
@@ -13,7 +13,9 @@ export interface LoaderArgs<Params extends Record<string, string> = Record<strin
13
13
  /**
14
14
  * represents an application page loader function.
15
15
  */
16
- export type LoaderFunction<ResponseData = unknown, Params extends Record<string, string> = Record<string, string>> = (args: LoaderArgs<Params>) => Promise<ResponseData> | ResponseData;
16
+ export type LoaderFunction<ResponseData = unknown, Params extends Record<string, string> = Record<string, string>> = (
17
+ args: LoaderArgs<Params>,
18
+ ) => Promise<ResponseData> | ResponseData;
17
19
 
18
20
  /**
19
21
  * extracts and unwraps the true data structure returned by a server function or loader.
@@ -27,9 +29,22 @@ export type GuardContext = {
27
29
  url: string;
28
30
  };
29
31
 
30
- export type GuardResult = void | undefined | { redirect: string; status?: 301 | 302 | 307 | 308 } | { status: 401 | 403 | 404 | 500; body?: string };
32
+ export type GuardResult =
33
+ | void
34
+ | undefined
35
+ | { redirect: string; status?: 301 | 302 | 307 | 308 }
36
+ | { status: 401 | 403 | 404 | 500; body?: string };
31
37
 
32
- export type GuardFn = (ctx: { params: Record<string, string>; request: Request; url: string }) => void | undefined | { redirect: string; status?: RedirectStatusCode } | { status: number; body?: string } | Promise<void | undefined | { redirect: string; status?: RedirectStatusCode } | { status: number; body?: string }>;
38
+ export type GuardFn = (ctx: {
39
+ params: Record<string, string>;
40
+ request: Request;
41
+ url: string;
42
+ }) =>
43
+ | void
44
+ | undefined
45
+ | { redirect: string; status?: RedirectStatusCode }
46
+ | { status: number; body?: string }
47
+ | Promise<void | undefined | { redirect: string; status?: RedirectStatusCode } | { status: number; body?: string }>;
33
48
 
34
49
  export type ServerFunction<Args extends unknown[], Return> = ((...args: Args) => Promise<Return>) & {
35
50
  id?: string;
@@ -28,7 +28,9 @@ async function resolveConfigPort() {
28
28
  return userConfig.port;
29
29
  }
30
30
  } catch (err) {
31
- console.warn(`[HMR test warning]: failed parsing anaemia.config.ts, falling back to port 3000. Error: ${err.message}`);
31
+ console.warn(
32
+ `[HMR test warning]: failed parsing anaemia.config.ts, falling back to port 3000. Error: ${err.message}`,
33
+ );
32
34
  }
33
35
  }
34
36
 
@@ -119,11 +121,18 @@ test("integration - dev server HMR & hydration lifecycle", async (t) => {
119
121
 
120
122
  const titleText = await page.textContent("h1");
121
123
  assert.equal(titleText.trim(), "anaemia");
122
- assert.equal(consoleErrors.length, 0, `Errors during initial render: ${consoleErrors.map((e) => e.message).join(", ")}`);
124
+ assert.equal(
125
+ consoleErrors.length,
126
+ 0,
127
+ `Errors during initial render: ${consoleErrors.map((e) => e.message).join(", ")}`,
128
+ );
123
129
  });
124
130
 
125
131
  await t.test("should push hot module updates to browser when files change", async () => {
126
- const updatedContent = originalComponentContent.replace(`<h1 class={styles.title}>anaemia</h1>`, `<h1 class={styles.title}>anaemia updated!</h1>`);
132
+ const updatedContent = originalComponentContent.replace(
133
+ `<h1 class={styles.title}>anaemia</h1>`,
134
+ `<h1 class={styles.title}>anaemia updated!</h1>`,
135
+ );
127
136
 
128
137
  assert.notEqual(originalComponentContent, updatedContent, "Regex failed to modify component text.");
129
138
  fs.writeFileSync(componentPath, updatedContent, "utf-8");
@@ -134,7 +143,7 @@ test("integration - dev server HMR & hydration lifecycle", async (t) => {
134
143
  const h1 = document.querySelector("h1");
135
144
  return h1 && h1.textContent.trim() === "anaemia updated!";
136
145
  },
137
- { timeout: 10000 }
146
+ { timeout: 10000 },
138
147
  );
139
148
  });
140
149
 
@@ -145,6 +154,10 @@ test("integration - dev server HMR & hydration lifecycle", async (t) => {
145
154
 
146
155
  const titleText = await page.textContent("h1");
147
156
  assert.equal(titleText.trim(), "anaemia updated!");
148
- assert.equal(consoleErrors.length, 0, `Errors detected after page refresh: ${consoleErrors.map((e) => e.message).join(", ")}`);
157
+ assert.equal(
158
+ consoleErrors.length,
159
+ 0,
160
+ `Errors detected after page refresh: ${consoleErrors.map((e) => e.message).join(", ")}`,
161
+ );
149
162
  });
150
163
  });
@@ -68,4 +68,4 @@ test("asyncRpcCall returns undefined when no SSR store exists", async () => {
68
68
  const fn = $$executeClientRpc("missing");
69
69
  const result = await fn("someArg");
70
70
  assert.equal(result, undefined);
71
- });
71
+ });
@@ -1,10 +1,6 @@
1
1
  import assert from "node:assert/strict";
2
2
  import test from "node:test";
3
- import {
4
- runOnServer,
5
- serverFunctionsRegistry,
6
- ssrStorage,
7
- } from "../dist/runtime/context.js";
3
+ import { runOnServer, serverFunctionsRegistry, ssrStorage } from "../dist/runtime/context.js";
8
4
  import { createRouteRequest } from "../dist/runtime/route-request.js";
9
5
 
10
6
  test("runOnServer returns a callable function and registers the original implementation", async () => {
@@ -26,7 +22,7 @@ test("runOnServer records SSR results by id and argument list for hydration", as
26
22
 
27
23
  assert.deepEqual(store.get("__SERVER_FUNCTION_DATA__"), {
28
24
  greet: {
29
- "[\"Ada\"]": "hello Ada",
25
+ '["Ada"]': "hello Ada",
30
26
  },
31
27
  });
32
28
  });
package/tsconfig.json CHANGED
@@ -9,4 +9,4 @@
9
9
  "types": ["node"]
10
10
  },
11
11
  "include": ["src/**/*"]
12
- }
12
+ }