@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.
- package/LICENSE +1 -1
- package/README.md +1 -1
- package/dist/config.d.ts +8 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/plugins/lightningcss.js +4 -4
- package/dist/runtime/context.d.ts.map +1 -1
- package/dist/runtime/context.js +5 -4
- package/dist/runtime/entry-client.jsx +4 -5
- package/dist/runtime/entry-server.d.ts +1 -2
- package/dist/runtime/entry-server.d.ts.map +1 -1
- package/dist/runtime/entry-server.jsx +18 -321
- package/dist/runtime/resources.d.ts.map +1 -1
- package/dist/runtime/resources.js +2 -1
- package/dist/runtime/route-data.d.ts.map +1 -1
- package/dist/runtime/route-data.js +4 -3
- package/dist/runtime/route-request.d.ts.map +1 -1
- package/dist/runtime/route-request.js +3 -2
- package/dist/runtime/rpc-client.d.ts.map +1 -1
- package/dist/runtime/rpc-client.js +10 -9
- package/dist/runtime/server/app.d.ts +22 -0
- package/dist/runtime/server/app.d.ts.map +1 -0
- package/dist/runtime/server/app.js +21 -0
- package/dist/runtime/server/assets.d.ts +5 -0
- package/dist/runtime/server/assets.d.ts.map +1 -0
- package/dist/runtime/server/assets.js +48 -0
- package/dist/runtime/server/boot.d.ts +4 -0
- package/dist/runtime/server/boot.d.ts.map +1 -0
- package/dist/runtime/server/boot.js +6 -0
- package/dist/runtime/server/env.d.ts +3 -0
- package/dist/runtime/server/env.d.ts.map +1 -0
- package/dist/runtime/server/env.js +14 -0
- package/dist/runtime/server/guards.d.ts +29 -0
- package/dist/runtime/server/guards.d.ts.map +1 -0
- package/dist/runtime/server/guards.js +12 -0
- package/dist/runtime/server/html.d.ts +15 -0
- package/dist/runtime/server/html.d.ts.map +1 -0
- package/dist/runtime/server/html.js +62 -0
- package/dist/runtime/server/hydration.d.ts +3 -0
- package/dist/runtime/server/hydration.d.ts.map +1 -0
- package/dist/runtime/server/hydration.js +20 -0
- package/dist/runtime/server/manifest.d.ts +14 -0
- package/dist/runtime/server/manifest.d.ts.map +1 -0
- package/dist/runtime/server/manifest.js +59 -0
- package/dist/runtime/server/render-request.d.ts +21 -0
- package/dist/runtime/server/render-request.d.ts.map +1 -0
- package/dist/runtime/server/render-request.jsx +170 -0
- package/dist/runtime/server/route-match.d.ts +14 -0
- package/dist/runtime/server/route-match.d.ts.map +1 -0
- package/dist/runtime/server/route-match.js +35 -0
- package/dist/runtime/server/rpc.d.ts +3 -0
- package/dist/runtime/server/rpc.d.ts.map +1 -0
- package/dist/runtime/server/rpc.js +32 -0
- package/dist/runtime/server/types.d.ts +33 -0
- package/dist/runtime/server/types.d.ts.map +1 -0
- package/dist/runtime/server/types.js +1 -0
- package/dist/runtime/shared/constants.d.ts +8 -0
- package/dist/runtime/shared/constants.d.ts.map +1 -0
- package/dist/runtime/shared/constants.js +7 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +11 -7
- package/src/anaemia.d.ts +75 -0
- package/src/config.ts +9 -1
- package/src/index.ts +9 -1
- package/src/plugins/index.ts +1 -1
- package/src/plugins/lightningcss.ts +11 -11
- package/src/runtime/context.browser.ts +1 -1
- package/src/runtime/context.ts +6 -5
- package/src/runtime/entry-client.tsx +5 -8
- package/src/runtime/entry-server.tsx +19 -373
- package/src/runtime/resources.ts +3 -2
- package/src/runtime/route-data.ts +6 -5
- package/src/runtime/route-request.ts +4 -3
- package/src/runtime/rpc-client.ts +14 -13
- package/src/runtime/server/app.ts +44 -0
- package/src/runtime/server/assets.ts +69 -0
- package/src/runtime/server/boot.ts +9 -0
- package/src/runtime/server/env.ts +17 -0
- package/src/runtime/server/guards.ts +26 -0
- package/src/runtime/server/html.ts +84 -0
- package/src/runtime/server/hydration.ts +26 -0
- package/src/runtime/server/manifest.ts +69 -0
- package/src/runtime/server/render-request.tsx +230 -0
- package/src/runtime/server/route-match.ts +45 -0
- package/src/runtime/server/rpc.ts +34 -0
- package/src/runtime/server/types.ts +36 -0
- package/src/runtime/shared/constants.ts +7 -0
- package/src/runtime/webpack.d.ts +1 -1
- package/src/types.ts +18 -3
- package/test/integration/hmr.test.mjs +18 -5
- package/test/rpc-client.test.mjs +1 -1
- package/test/run-on-server.test.mjs +2 -6
- package/tsconfig.json +1 -1
|
@@ -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 @@
|
|
|
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__";
|
package/dist/types.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
"types": "./dist/plugins/index.d.ts",
|
|
29
29
|
"import": "./dist/plugins/index.js"
|
|
30
30
|
},
|
|
31
|
+
"./client": {
|
|
32
|
+
"types": "./anaemia.d.ts",
|
|
33
|
+
"default": "./anaemia.d.ts"
|
|
34
|
+
},
|
|
31
35
|
"./package.json": "./package.json"
|
|
32
36
|
},
|
|
33
37
|
"dependencies": {
|
|
@@ -36,6 +40,12 @@
|
|
|
36
40
|
"@solidjs/router": "^0.16.1",
|
|
37
41
|
"hono": "^4.12.23"
|
|
38
42
|
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/babel__core": "^7.20.5",
|
|
45
|
+
"@types/node": "^25.9.1",
|
|
46
|
+
"jiti": "^2.7.0",
|
|
47
|
+
"playwright": "^1.60.0"
|
|
48
|
+
},
|
|
39
49
|
"peerDependencies": {
|
|
40
50
|
"@rspack/core": "*",
|
|
41
51
|
"lightningcss": "^1.32.0",
|
|
@@ -49,12 +59,6 @@
|
|
|
49
59
|
"optional": true
|
|
50
60
|
}
|
|
51
61
|
},
|
|
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
62
|
"scripts": {
|
|
59
63
|
"build": "tsc",
|
|
60
64
|
"test": "pnpm run build && node --test test/*.test.mjs",
|
package/src/anaemia.d.ts
ADDED
|
@@ -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 {
|
|
8
|
+
export type {
|
|
9
|
+
LoaderArgs,
|
|
10
|
+
LoaderFunction,
|
|
11
|
+
InferServerData,
|
|
12
|
+
GuardContext,
|
|
13
|
+
GuardResult,
|
|
14
|
+
GuardFn,
|
|
15
|
+
ServerFunction,
|
|
16
|
+
} from "./types.js";
|
package/src/plugins/index.ts
CHANGED
|
@@ -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
|
|
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
|
|
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;
|
package/src/runtime/context.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { SERVER_FUNCTION_DATA_KEY } from "./shared/constants.js";
|
|
2
3
|
|
|
3
4
|
type AnyFn = (...args: unknown[]) => unknown;
|
|
4
5
|
|
|
@@ -7,17 +8,17 @@ export const ssrStorage = new AsyncLocalStorage<Map<string, unknown>>();
|
|
|
7
8
|
(globalThis as unknown as Record<string, unknown>).__ANAEMIA_SERVER_STORAGE__ = ssrStorage;
|
|
8
9
|
|
|
9
10
|
export function runOnServer<T extends AnyFn>(backendFn: T, id?: string): T & { id: string } {
|
|
10
|
-
const hashId = id
|
|
11
|
+
const hashId = id ?? crypto.randomUUID();
|
|
11
12
|
serverFunctionsRegistry.set(hashId, backendFn);
|
|
12
13
|
|
|
13
14
|
const rpcProxy = async function (...args: unknown[]) {
|
|
14
15
|
const result = await backendFn(...args);
|
|
15
16
|
const store = ssrStorage.getStore();
|
|
16
17
|
if (store && hashId) {
|
|
17
|
-
if (!store.has(
|
|
18
|
-
store.set(
|
|
18
|
+
if (!store.has(SERVER_FUNCTION_DATA_KEY)) {
|
|
19
|
+
store.set(SERVER_FUNCTION_DATA_KEY, {});
|
|
19
20
|
}
|
|
20
|
-
const functionCache = store.get(
|
|
21
|
+
const functionCache = store.get(SERVER_FUNCTION_DATA_KEY) as Record<string, Record<string, unknown> | undefined>;
|
|
21
22
|
if (!functionCache[hashId]) {
|
|
22
23
|
functionCache[hashId] = {};
|
|
23
24
|
}
|
|
@@ -28,4 +29,4 @@ export function runOnServer<T extends AnyFn>(backendFn: T, id?: string): T & { i
|
|
|
28
29
|
};
|
|
29
30
|
rpcProxy.id = hashId;
|
|
30
31
|
return rpcProxy as unknown as T & { id: string };
|
|
31
|
-
}
|
|
32
|
+
}
|
|
@@ -1,12 +1,11 @@
|
|
|
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
|
|
|
4
5
|
// @ts-expect-error - resolved by Rspack
|
|
5
6
|
import App, { preloadActiveClientRoute } from "anaemia-user-app";
|
|
6
7
|
|
|
7
|
-
const mountTarget = document.querySelector(
|
|
8
|
-
"[anaemia-entry]"
|
|
9
|
-
) as HTMLElement | null;
|
|
8
|
+
const mountTarget = document.querySelector(ENTRY_SELECTOR) as HTMLElement | null;
|
|
10
9
|
|
|
11
10
|
if (!mountTarget) {
|
|
12
11
|
throw new Error("[anaemia] missing mount target");
|
|
@@ -17,9 +16,7 @@ const root = mountTarget;
|
|
|
17
16
|
async function start() {
|
|
18
17
|
await preloadActiveClientRoute(window.location.pathname);
|
|
19
18
|
|
|
20
|
-
const mount = root.hasChildNodes()
|
|
21
|
-
? hydrate
|
|
22
|
-
: render;
|
|
19
|
+
const mount = root.hasChildNodes() ? hydrate : render;
|
|
23
20
|
|
|
24
21
|
mount(
|
|
25
22
|
() => (
|
|
@@ -27,8 +24,8 @@ async function start() {
|
|
|
27
24
|
<App />
|
|
28
25
|
</Router>
|
|
29
26
|
),
|
|
30
|
-
root
|
|
27
|
+
root,
|
|
31
28
|
);
|
|
32
29
|
}
|
|
33
30
|
|
|
34
|
-
start();
|
|
31
|
+
await start();
|