@devlusoft/devix 0.1.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 (109) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +216 -0
  3. package/bin/devix.js +2 -0
  4. package/dist/cli/build.d.ts +1 -0
  5. package/dist/cli/build.js +286 -0
  6. package/dist/cli/build.js.map +7 -0
  7. package/dist/cli/dev.d.ts +1 -0
  8. package/dist/cli/dev.js +361 -0
  9. package/dist/cli/dev.js.map +7 -0
  10. package/dist/cli/generate.d.ts +1 -0
  11. package/dist/cli/generate.js +389 -0
  12. package/dist/cli/generate.js.map +7 -0
  13. package/dist/cli/index.d.ts +2 -0
  14. package/dist/cli/index.js +649 -0
  15. package/dist/cli/index.js.map +7 -0
  16. package/dist/cli/start.d.ts +1 -0
  17. package/dist/cli/start.js +78 -0
  18. package/dist/cli/start.js.map +7 -0
  19. package/dist/config.d.ts +21 -0
  20. package/dist/config.js +17 -0
  21. package/dist/config.js.map +7 -0
  22. package/dist/runtime/api-context.d.ts +20 -0
  23. package/dist/runtime/api-context.js +18 -0
  24. package/dist/runtime/api-context.js.map +7 -0
  25. package/dist/runtime/client-router.d.ts +13 -0
  26. package/dist/runtime/client-router.js +59 -0
  27. package/dist/runtime/client-router.js.map +7 -0
  28. package/dist/runtime/context.d.ts +27 -0
  29. package/dist/runtime/context.js +15 -0
  30. package/dist/runtime/context.js.map +7 -0
  31. package/dist/runtime/error-boundary.d.ts +19 -0
  32. package/dist/runtime/error-boundary.js +37 -0
  33. package/dist/runtime/error-boundary.js.map +7 -0
  34. package/dist/runtime/head.d.ts +3 -0
  35. package/dist/runtime/head.js +69 -0
  36. package/dist/runtime/head.js.map +7 -0
  37. package/dist/runtime/index.d.ts +5 -0
  38. package/dist/runtime/index.js +300 -0
  39. package/dist/runtime/index.js.map +7 -0
  40. package/dist/runtime/link.d.ts +8 -0
  41. package/dist/runtime/link.js +43 -0
  42. package/dist/runtime/link.js.map +7 -0
  43. package/dist/runtime/metadata.d.ts +10 -0
  44. package/dist/runtime/metadata.js +22 -0
  45. package/dist/runtime/metadata.js.map +7 -0
  46. package/dist/runtime/router-provider.d.ts +22 -0
  47. package/dist/runtime/router-provider.js +259 -0
  48. package/dist/runtime/router-provider.js.map +7 -0
  49. package/dist/server/api-router.d.ts +21 -0
  50. package/dist/server/api-router.js +64 -0
  51. package/dist/server/api-router.js.map +7 -0
  52. package/dist/server/api.d.ts +2 -0
  53. package/dist/server/api.js +123 -0
  54. package/dist/server/api.js.map +7 -0
  55. package/dist/server/collect-css.d.ts +2 -0
  56. package/dist/server/collect-css.js +15 -0
  57. package/dist/server/collect-css.js.map +7 -0
  58. package/dist/server/index.d.ts +6 -0
  59. package/dist/server/index.js +133 -0
  60. package/dist/server/index.js.map +7 -0
  61. package/dist/server/pages-router.d.ts +21 -0
  62. package/dist/server/pages-router.js +64 -0
  63. package/dist/server/pages-router.js.map +7 -0
  64. package/dist/server/render.d.ts +34 -0
  65. package/dist/server/render.js +306 -0
  66. package/dist/server/render.js.map +7 -0
  67. package/dist/server/routes.d.ts +11 -0
  68. package/dist/server/routes.js +42 -0
  69. package/dist/server/routes.js.map +7 -0
  70. package/dist/server/types.d.ts +49 -0
  71. package/dist/server/types.js +1 -0
  72. package/dist/server/types.js.map +7 -0
  73. package/dist/types.d.ts +35 -0
  74. package/dist/types.js +1 -0
  75. package/dist/types.js.map +7 -0
  76. package/dist/utils/async.d.ts +1 -0
  77. package/dist/utils/async.js +14 -0
  78. package/dist/utils/async.js.map +7 -0
  79. package/dist/utils/banner.d.ts +1 -0
  80. package/dist/utils/banner.js +34 -0
  81. package/dist/utils/banner.js.map +7 -0
  82. package/dist/utils/duration.d.ts +1 -0
  83. package/dist/utils/duration.js +22 -0
  84. package/dist/utils/duration.js.map +7 -0
  85. package/dist/utils/html.d.ts +2 -0
  86. package/dist/utils/html.js +12 -0
  87. package/dist/utils/html.js.map +7 -0
  88. package/dist/utils/patterns.d.ts +1 -0
  89. package/dist/utils/patterns.js +8 -0
  90. package/dist/utils/patterns.js.map +7 -0
  91. package/dist/vite/codegen/api.d.ts +6 -0
  92. package/dist/vite/codegen/api.js +23 -0
  93. package/dist/vite/codegen/api.js.map +7 -0
  94. package/dist/vite/codegen/client-routes.d.ts +6 -0
  95. package/dist/vite/codegen/client-routes.js +36 -0
  96. package/dist/vite/codegen/client-routes.js.map +7 -0
  97. package/dist/vite/codegen/context.d.ts +1 -0
  98. package/dist/vite/codegen/context.js +10 -0
  99. package/dist/vite/codegen/context.js.map +7 -0
  100. package/dist/vite/codegen/entry-client.d.ts +5 -0
  101. package/dist/vite/codegen/entry-client.js +64 -0
  102. package/dist/vite/codegen/entry-client.js.map +7 -0
  103. package/dist/vite/codegen/render.d.ts +6 -0
  104. package/dist/vite/codegen/render.js +31 -0
  105. package/dist/vite/codegen/render.js.map +7 -0
  106. package/dist/vite/index.d.ts +3 -0
  107. package/dist/vite/index.js +225 -0
  108. package/dist/vite/index.js.map +7 -0
  109. package/package.json +77 -0
@@ -0,0 +1,259 @@
1
+ // src/runtime/router-provider.tsx
2
+ import { useCallback, useContext, useEffect, useRef, useState } from "react";
3
+ import { RouterContext as RouterContext2 } from "virtual:devix/context";
4
+ import { getDefaultErrorPage, loadErrorPage, matchClientRoute } from "virtual:devix/client-routes";
5
+
6
+ // src/runtime/head.tsx
7
+ import { Fragment, jsx } from "react/jsx-runtime";
8
+ function collectTags(metadata, viewport) {
9
+ const tags = [];
10
+ if (metadata.title)
11
+ tags.push({ tag: "title", children: metadata.title });
12
+ if (metadata.description)
13
+ tags.push({ tag: "meta", name: "description", content: metadata.description });
14
+ if (metadata.keywords?.length)
15
+ tags.push({ tag: "meta", name: "keywords", content: metadata.keywords.join(", ") });
16
+ const ogTitle = metadata.og?.title ?? metadata.title;
17
+ if (ogTitle) tags.push({ tag: "meta", property: "og:title", content: ogTitle });
18
+ const ogDesc = metadata.og?.description ?? metadata.description;
19
+ if (ogDesc) tags.push({ tag: "meta", property: "og:description", content: ogDesc });
20
+ if (metadata.og?.image) tags.push({ tag: "meta", property: "og:image", content: metadata.og.image });
21
+ if (metadata.og?.type) tags.push({ tag: "meta", property: "og:type", content: metadata.og.type });
22
+ if (metadata.og?.url) tags.push({ tag: "meta", property: "og:url", content: metadata.og.url });
23
+ const twTitle = metadata.twitter?.title ?? metadata.title;
24
+ if (twTitle) tags.push({ tag: "meta", name: "twitter:title", content: twTitle });
25
+ const twDesc = metadata.twitter?.description ?? metadata.description;
26
+ if (twDesc) tags.push({ tag: "meta", name: "twitter:description", content: twDesc });
27
+ if (metadata.twitter?.card) tags.push({
28
+ tag: "meta",
29
+ name: "twitter:card",
30
+ content: metadata.twitter.card
31
+ });
32
+ if (metadata.twitter?.image) tags.push({
33
+ tag: "meta",
34
+ name: "twitter:image",
35
+ content: metadata.twitter.image
36
+ });
37
+ if (metadata.twitter?.creator) tags.push({
38
+ tag: "meta",
39
+ name: "twitter:creator",
40
+ content: metadata.twitter.creator
41
+ });
42
+ if (metadata.canonical) tags.push({ tag: "link", rel: "canonical", href: metadata.canonical });
43
+ if (metadata.robots) tags.push({ tag: "meta", name: "robots", content: metadata.robots });
44
+ if (metadata.alternates) {
45
+ for (const [lang, href] of Object.entries(metadata.alternates))
46
+ tags.push({ tag: "link", rel: "alternate", href, hrefLang: lang });
47
+ }
48
+ if (viewport) {
49
+ const parts = [];
50
+ if (viewport.width !== void 0) parts.push(`width=${viewport.width}`);
51
+ if (viewport.initialScale !== void 0) parts.push(`initial-scale=${viewport.initialScale}`);
52
+ if (viewport.maximumScale !== void 0) parts.push(`maximum-scale=${viewport.maximumScale}`);
53
+ if (viewport.userScalable !== void 0) parts.push(`user-scalable=${viewport.userScalable ? "yes" : "no"}`);
54
+ if (parts.length) tags.push({ tag: "meta", name: "viewport", content: parts.join(", ") });
55
+ if (viewport.themeColor) tags.push({
56
+ tag: "meta",
57
+ name: "theme-color",
58
+ content: viewport.themeColor
59
+ });
60
+ }
61
+ return tags;
62
+ }
63
+ function buildHeadNodes(metadata, viewport) {
64
+ const tags = collectTags(metadata, viewport);
65
+ return /* @__PURE__ */ jsx(Fragment, { children: tags.map((t, i) => {
66
+ if (t.tag === "title") return /* @__PURE__ */ jsx("title", { children: t.children }, i);
67
+ if (t.tag === "link") return /* @__PURE__ */ jsx("link", { rel: t.rel, href: t.href, hrefLang: t.hrefLang }, i);
68
+ return /* @__PURE__ */ jsx("meta", { name: t.name, property: t.property, content: t.content }, i);
69
+ }) });
70
+ }
71
+
72
+ // src/runtime/context.tsx
73
+ import { createContext } from "react";
74
+ var g = globalThis;
75
+ g.__devix_RouterContext__ ??= createContext(null);
76
+ var RouterContext = g.__devix_RouterContext__;
77
+ g.__devix_PageMetaContext__ ??= createContext(null);
78
+ g.__devix_RouteDataContext__ ??= createContext(null);
79
+ var PageMetaContext = g.__devix_PageMetaContext__;
80
+ var RouteDataContext = g.__devix_RouteDataContext__;
81
+
82
+ // src/runtime/error-boundary.tsx
83
+ import { Component } from "react";
84
+ import { jsx as jsx2 } from "react/jsx-runtime";
85
+ var DevixErrorBoundary = class extends Component {
86
+ state = { error: null };
87
+ static getDerivedStateFromError(err) {
88
+ if (err instanceof DevixError) {
89
+ return {
90
+ error: { statusCode: err.statusCode, message: err.message }
91
+ };
92
+ }
93
+ return {
94
+ error: { statusCode: 500, message: err instanceof Error ? err.message : "Unknown error" }
95
+ };
96
+ }
97
+ render() {
98
+ if (this.state.error && this.props.ErrorPage) {
99
+ return /* @__PURE__ */ jsx2(this.props.ErrorPage, { ...this.state.error });
100
+ }
101
+ if (this.state.error) {
102
+ return /* @__PURE__ */ jsx2("h1", { children: this.state.error.statusCode });
103
+ }
104
+ return this.props.children;
105
+ }
106
+ };
107
+ var DevixError = class extends Error {
108
+ statusCode;
109
+ constructor(statusCode, message) {
110
+ super(message);
111
+ this.statusCode = statusCode;
112
+ }
113
+ };
114
+
115
+ // src/runtime/router-provider.tsx
116
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
117
+ function useRouter() {
118
+ return useContext(RouterContext2);
119
+ }
120
+ function useNavigate() {
121
+ const ctx = useContext(RouterContext2);
122
+ if (!ctx) throw new Error("useNavigate must be used within a RouterProvider");
123
+ return ctx.navigate;
124
+ }
125
+ function useParams() {
126
+ const ctx = useContext(RouteDataContext);
127
+ if (!ctx) throw new Error("useParams must be used within a route or layout");
128
+ return ctx.params;
129
+ }
130
+ function useLoaderData() {
131
+ const ctx = useContext(RouteDataContext);
132
+ if (!ctx) throw new Error("useLoaderData must be used within a route or layout");
133
+ return ctx.loaderData;
134
+ }
135
+ function RouterProvider({
136
+ initialData,
137
+ initialParams,
138
+ initialPage,
139
+ initialLayouts = [],
140
+ initialLayoutsData = [],
141
+ initialMeta,
142
+ initialViewport,
143
+ initialError,
144
+ initialErrorPage,
145
+ clientEntry
146
+ }) {
147
+ const [state, setState] = useState({
148
+ pathname: window.location.pathname,
149
+ params: initialParams,
150
+ loaderData: initialData,
151
+ layoutsData: initialLayoutsData,
152
+ Page: initialPage,
153
+ layouts: initialLayouts,
154
+ metadata: initialMeta ?? null,
155
+ viewport: initialViewport,
156
+ pendingError: initialError,
157
+ ErrorPage: initialErrorPage
158
+ });
159
+ const navigatingRef = useRef(null);
160
+ const [isNavigating, setIsNavigating] = useState(false);
161
+ const loadRoute = useCallback(async (to, controller) => {
162
+ const pathname = to.split("?")[0];
163
+ const matched = matchClientRoute(pathname);
164
+ if (!matched) {
165
+ const ErrorPage = await loadErrorPage() ?? getDefaultErrorPage();
166
+ setState((prev) => ({
167
+ ...prev,
168
+ pathname,
169
+ pendingError: { statusCode: 404, message: "Not found" },
170
+ ErrorPage: ErrorPage ?? void 0
171
+ }));
172
+ return;
173
+ }
174
+ const [pageMod, ...layoutMods] = await Promise.all([
175
+ matched.load(),
176
+ ...matched.loadLayouts.map((l) => l())
177
+ ]);
178
+ if (controller.signal.aborted) return;
179
+ if (!pageMod.default) return;
180
+ const dataRes = await fetch(`/_data${to}`, {
181
+ headers: { Accept: "application/json" },
182
+ signal: controller.signal
183
+ });
184
+ if (controller.signal.aborted) return;
185
+ if (!dataRes.ok) {
186
+ if (dataRes.status === 404) {
187
+ window.location.href = to;
188
+ return;
189
+ }
190
+ console.error(`/_data${to} returned ${dataRes.status}`);
191
+ return;
192
+ }
193
+ const data = await dataRes.json();
194
+ window.scrollTo(0, 0);
195
+ setState({
196
+ pathname,
197
+ params: data.params ?? {},
198
+ loaderData: data.loaderData,
199
+ layoutsData: (data.layouts ?? []).map((l) => l.loaderData),
200
+ Page: pageMod.default,
201
+ layouts: layoutMods.map((m) => m.default),
202
+ metadata: data.metadata ?? null,
203
+ viewport: data.viewport
204
+ });
205
+ }, []);
206
+ const navigate = useCallback(async (to) => {
207
+ navigatingRef.current?.abort();
208
+ const controller = new AbortController();
209
+ navigatingRef.current = controller;
210
+ setIsNavigating(true);
211
+ try {
212
+ window.history.pushState(null, "", to);
213
+ await loadRoute(to, controller);
214
+ } finally {
215
+ if (!controller.signal.aborted) setIsNavigating(false);
216
+ }
217
+ }, [loadRoute]);
218
+ useEffect(() => {
219
+ const handlePop = () => {
220
+ navigatingRef.current?.abort();
221
+ const controller = new AbortController();
222
+ navigatingRef.current = controller;
223
+ const to = window.location.pathname + window.location.search;
224
+ loadRoute(to, controller).catch((err) => {
225
+ if (err.name !== "AbortError") console.error("[router] popstate error:", err);
226
+ });
227
+ };
228
+ window.addEventListener("popstate", handlePop);
229
+ return () => window.removeEventListener("popstate", handlePop);
230
+ }, [loadRoute]);
231
+ let content;
232
+ if (state.pendingError) {
233
+ content = state.ErrorPage ? /* @__PURE__ */ jsx3(state.ErrorPage, { ...state.pendingError }) : /* @__PURE__ */ jsx3("h1", { children: state.pendingError.statusCode });
234
+ } else {
235
+ let tree = /* @__PURE__ */ jsx3(RouteDataContext, { value: { loaderData: state.loaderData, params: state.params }, children: /* @__PURE__ */ jsx3(state.Page, { data: state.loaderData, params: state.params, url: state.pathname }) });
236
+ for (let i = state.layouts.length - 1; i >= 0; i--) {
237
+ const Layout = state.layouts[i];
238
+ const layoutData = state.layoutsData[i];
239
+ tree = /* @__PURE__ */ jsx3(RouteDataContext, { value: { loaderData: layoutData, params: state.params }, children: /* @__PURE__ */ jsx3(Layout, { data: layoutData, params: state.params, children: tree }) });
240
+ }
241
+ content = /* @__PURE__ */ jsx3(DevixErrorBoundary, { ErrorPage: state.ErrorPage, children: tree }, state.pathname);
242
+ }
243
+ return /* @__PURE__ */ jsxs(PageMetaContext, { value: {
244
+ metadata: state.metadata,
245
+ viewport: state.viewport,
246
+ clientEntry
247
+ }, children: [
248
+ state.metadata && buildHeadNodes(state.metadata, state.viewport),
249
+ /* @__PURE__ */ jsx3(RouterContext2, { value: { ...state, isNavigating, navigate }, children: content })
250
+ ] });
251
+ }
252
+ export {
253
+ RouterProvider,
254
+ useLoaderData,
255
+ useNavigate,
256
+ useParams,
257
+ useRouter
258
+ };
259
+ //# sourceMappingURL=router-provider.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/runtime/router-provider.tsx", "../../src/runtime/head.tsx", "../../src/runtime/context.tsx", "../../src/runtime/error-boundary.tsx"],
4
+ "sourcesContent": ["import {ComponentType, ReactNode, useCallback, useContext, useEffect, useRef, useState} from \"react\";\nimport {RouterContext} from 'virtual:devix/context'\nimport {ErrorProps, LayoutProps, PageProps} from \"../server/types\";\nimport {Metadata, Viewport} from \"../types\";\nimport {getDefaultErrorPage, loadErrorPage, matchClientRoute} from \"virtual:devix/client-routes\";\nimport {buildHeadNodes} from \"./head\";\nimport {PageMetaContext, RouteDataContext} from \"./context\";\nimport {DevixErrorBoundary} from \"./error-boundary\";\n\ninterface RouteState {\n pathname: string\n params: Record<string, string>\n loaderData: unknown\n layoutsData: unknown[]\n Page: ComponentType<PageProps>\n layouts: ComponentType<LayoutProps>[]\n metadata: Metadata | null\n viewport?: Viewport\n pendingError?: ErrorProps\n ErrorPage?: ComponentType<ErrorProps>\n}\n\nexport function useRouter() {\n return useContext(RouterContext)\n}\n\nexport function useNavigate() {\n const ctx = useContext(RouterContext)\n if (!ctx) throw new Error(\"useNavigate must be used within a RouterProvider\")\n return ctx.navigate\n}\n\nexport function useParams<T extends Record<string, string>>() {\n const ctx = useContext(RouteDataContext)\n if (!ctx) throw new Error(\"useParams must be used within a route or layout\")\n return ctx.params as T\n}\n\ntype LoaderReturnType<T> = T extends (...args: any[]) => Promise<infer R>\n ? R\n : T extends (...args: any[]) => infer R\n ? R\n : T\n\nexport function useLoaderData<T>() {\n const ctx = useContext(RouteDataContext)\n if (!ctx) throw new Error(\"useLoaderData must be used within a route or layout\")\n return ctx.loaderData as LoaderReturnType<T>\n}\n\n\ninterface RouterProviderProps {\n initialData: unknown\n initialParams: Record<string, string>\n initialPage: ComponentType<PageProps>\n initialLayouts?: ComponentType<LayoutProps>[]\n initialLayoutsData?: unknown[]\n initialMeta?: Metadata | null\n initialViewport?: Viewport\n initialError?: ErrorProps\n initialErrorPage?: ComponentType<ErrorProps>\n clientEntry: string\n}\n\nexport function RouterProvider({\n initialData,\n initialParams,\n initialPage,\n initialLayouts = [],\n initialLayoutsData = [],\n initialMeta,\n initialViewport,\n initialError,\n initialErrorPage,\n clientEntry,\n }: RouterProviderProps) {\n\n const [state, setState] = useState<RouteState>({\n pathname: window.location.pathname,\n params: initialParams,\n loaderData: initialData,\n layoutsData: initialLayoutsData,\n Page: initialPage,\n layouts: initialLayouts,\n metadata: initialMeta ?? null,\n viewport: initialViewport,\n pendingError: initialError,\n ErrorPage: initialErrorPage,\n })\n\n const navigatingRef = useRef<AbortController | null>(null)\n const [isNavigating, setIsNavigating] = useState(false)\n\n const loadRoute = useCallback(async (to: string, controller: AbortController) => {\n const pathname = to.split('?')[0]\n const matched = matchClientRoute(pathname)\n if (!matched) {\n const ErrorPage = await loadErrorPage() ?? getDefaultErrorPage()\n setState(prev => ({\n ...prev,\n pathname: pathname,\n pendingError: {statusCode: 404, message: 'Not found'},\n ErrorPage: ErrorPage ?? undefined,\n }))\n return\n }\n\n const [pageMod, ...layoutMods] = await Promise.all([\n matched.load(),\n ...matched.loadLayouts.map(l => l()),\n ])\n\n if (controller.signal.aborted) return\n if (!pageMod.default) return\n\n const dataRes = await fetch(`/_data${to}`, {\n headers: {Accept: 'application/json'},\n signal: controller.signal,\n })\n\n if (controller.signal.aborted) return\n\n if (!dataRes.ok) {\n if (dataRes.status === 404) {\n window.location.href = to\n return\n }\n console.error(`/_data${to} returned ${dataRes.status}`)\n return\n }\n\n const data = await dataRes.json()\n\n window.scrollTo(0, 0)\n setState({\n pathname,\n params: data.params ?? {},\n loaderData: data.loaderData,\n layoutsData: (data.layouts ?? []).map((l: any) => l.loaderData),\n Page: pageMod.default,\n layouts: layoutMods.map(m => m.default),\n metadata: data.metadata ?? null,\n viewport: data.viewport,\n })\n }, [])\n\n const navigate = useCallback(async (to: string) => {\n navigatingRef.current?.abort()\n const controller = new AbortController()\n navigatingRef.current = controller\n\n setIsNavigating(true)\n try {\n window.history.pushState(null, \"\", to)\n await loadRoute(to, controller)\n } finally {\n if (!controller.signal.aborted) setIsNavigating(false)\n }\n }, [loadRoute])\n\n useEffect(() => {\n const handlePop = () => {\n navigatingRef.current?.abort()\n const controller = new AbortController()\n navigatingRef.current = controller\n\n const to = window.location.pathname + window.location.search\n loadRoute(to, controller).catch(err => {\n if (err.name !== 'AbortError') console.error('[router] popstate error:', err)\n })\n }\n window.addEventListener(\"popstate\", handlePop)\n return () => window.removeEventListener(\"popstate\", handlePop)\n }, [loadRoute])\n\n let content: ReactNode\n\n if (state.pendingError) {\n content = state.ErrorPage\n ? <state.ErrorPage {...state.pendingError} />\n : <h1>{state.pendingError.statusCode}</h1>\n } else {\n let tree: ReactNode = (\n <RouteDataContext value={{loaderData: state.loaderData, params: state.params}}>\n <state.Page data={state.loaderData} params={state.params} url={state.pathname}/>\n </RouteDataContext>\n )\n\n for (let i = state.layouts.length - 1; i >= 0; i--) {\n const Layout = state.layouts[i]\n const layoutData = state.layoutsData[i]\n tree = (\n <RouteDataContext value={{loaderData: layoutData, params: state.params}}>\n <Layout data={layoutData} params={state.params}>{tree}</Layout>\n </RouteDataContext>\n )\n }\n\n content = (\n <DevixErrorBoundary key={state.pathname} ErrorPage={state.ErrorPage}>\n {tree}\n </DevixErrorBoundary>\n )\n }\n\n return (\n <PageMetaContext value={{\n metadata: state.metadata,\n viewport: state.viewport,\n clientEntry,\n }}>\n {state.metadata && buildHeadNodes(state.metadata, state.viewport)}\n <RouterContext value={{...state, isNavigating, navigate}}>\n {content}\n </RouterContext>\n </PageMetaContext>\n )\n}", "import {Metadata, Viewport} from \"../types\";\nimport {ReactNode} from \"react\";\n\ntype MetaTag =\n | { tag: 'title'; children: string }\n | { tag: 'meta'; name?: string; property?: string; content: string }\n | { tag: 'link'; rel: string; href: string; hrefLang?: string }\n\nfunction collectTags(metadata: Metadata, viewport?: Viewport): MetaTag[] {\n const tags: MetaTag[] = []\n\n if (metadata.title)\n tags.push({tag: 'title', children: metadata.title})\n if (metadata.description)\n tags.push({tag: 'meta', name: 'description', content: metadata.description})\n if (metadata.keywords?.length)\n tags.push({tag: 'meta', name: 'keywords', content: metadata.keywords.join(', ')})\n\n const ogTitle = metadata.og?.title ?? metadata.title\n if (ogTitle) tags.push({tag: 'meta', property: 'og:title', content: ogTitle})\n const ogDesc = metadata.og?.description ?? metadata.description\n if (ogDesc) tags.push({tag: 'meta', property: 'og:description', content: ogDesc})\n if (metadata.og?.image) tags.push({tag: 'meta', property: 'og:image', content: metadata.og.image})\n if (metadata.og?.type) tags.push({tag: 'meta', property: 'og:type', content: metadata.og.type})\n if (metadata.og?.url) tags.push({tag: 'meta', property: 'og:url', content: metadata.og.url})\n\n const twTitle = metadata.twitter?.title ?? metadata.title\n if (twTitle) tags.push({tag: 'meta', name: 'twitter:title', content: twTitle})\n const twDesc = metadata.twitter?.description ?? metadata.description\n if (twDesc) tags.push({tag: 'meta', name: 'twitter:description', content: twDesc})\n if (metadata.twitter?.card) tags.push({\n tag: 'meta', name: 'twitter:card', content:\n metadata.twitter.card\n })\n if (metadata.twitter?.image) tags.push({\n tag: 'meta', name: 'twitter:image', content:\n metadata.twitter.image\n })\n if (metadata.twitter?.creator) tags.push({\n tag: 'meta', name: 'twitter:creator', content:\n metadata.twitter.creator\n })\n\n if (metadata.canonical) tags.push({tag: 'link', rel: 'canonical', href: metadata.canonical})\n if (metadata.robots) tags.push({tag: 'meta', name: 'robots', content: metadata.robots})\n if (metadata.alternates) {\n for (const [lang, href] of Object.entries(metadata.alternates))\n tags.push({tag: 'link', rel: 'alternate', href, hrefLang: lang})\n }\n\n if (viewport) {\n const parts: string[] = []\n if (viewport.width !== undefined) parts.push(`width=${viewport.width}`)\n if (viewport.initialScale !== undefined) parts.push(`initial-scale=${viewport.initialScale}`)\n if (viewport.maximumScale !== undefined) parts.push(`maximum-scale=${viewport.maximumScale}`)\n if (viewport.userScalable !== undefined) parts.push(`user-scalable=${viewport.userScalable ? 'yes' :\n 'no'}`)\n if (parts.length) tags.push({tag: 'meta', name: 'viewport', content: parts.join(', ')})\n if (viewport.themeColor) tags.push({\n tag: 'meta', name: 'theme-color', content: viewport.themeColor\n })\n }\n\n return tags\n}\n\nexport function buildHeadNodes(metadata: Metadata, viewport?: Viewport): ReactNode {\n const tags = collectTags(metadata, viewport)\n\n return <>\n {tags.map((t, i) => {\n if (t.tag === 'title') return <title key={i}>{t.children}</title>\n if (t.tag === 'link') return <link key={i} rel={t.rel} href={t.href} hrefLang={t.hrefLang}/>\n return <meta key={i} name={t.name} property={t.property} content={t.content}/>\n })}\n </>\n}", "import {createContext, Context, ComponentType} from \"react\";\nimport {Metadata, Viewport} from \"../types\";\nimport {LayoutProps, PageProps} from \"../server/types\";\n\nexport interface RouterContextValue {\n pathname: string\n params: Record<string, string>\n loaderData: unknown\n layoutsData: unknown[]\n Page: ComponentType<PageProps>\n layouts: ComponentType<LayoutProps>[]\n metadata: Metadata | null\n viewport?: Viewport\n navigate: (to: string) => void\n isNavigating: boolean\n}\n\nexport interface PageMetaContextValue {\n metadata: Metadata | null\n viewport?: Viewport\n clientEntry?: string\n}\n\nexport interface RouteDataContextValue {\n loaderData: unknown\n params: Record<string, string>\n}\n\nconst g = globalThis as any\n\ng.__devix_RouterContext__ ??= createContext<RouterContextValue | null>(null)\nexport const RouterContext: Context<RouterContextValue | null> = g.__devix_RouterContext__\n\ng.__devix_PageMetaContext__ ??= createContext<PageMetaContextValue | null>(null)\ng.__devix_RouteDataContext__ ??= createContext<RouteDataContextValue | null>(null)\n\nexport const PageMetaContext: Context<PageMetaContextValue | null> = g.__devix_PageMetaContext__\nexport const RouteDataContext: Context<RouteDataContextValue | null> = g.__devix_RouteDataContext__\n\n", "import {Component, ComponentType, ReactNode} from \"react\";\nimport {ErrorProps} from \"../server/types\";\n\ninterface Props {\n ErrorPage?: ComponentType<ErrorProps>\n children: ReactNode\n}\n\ninterface State {\n error: ErrorProps | null\n}\n\nexport class DevixErrorBoundary extends Component<Props, State> {\n state: State = { error: null }\n\n static getDerivedStateFromError(err: unknown): State {\n if (err instanceof DevixError) {\n return {\n error: {statusCode: err.statusCode, message: err.message}\n }\n }\n return {\n error: {statusCode: 500, message: err instanceof Error ? err.message : 'Unknown error'}\n }\n }\n\n render() {\n if (this.state.error && this.props.ErrorPage) {\n return <this.props.ErrorPage {...this.state.error} />\n }\n if (this.state.error) {\n return <h1>{this.state.error.statusCode}</h1>\n }\n return this.props.children\n }\n}\n\nexport class DevixError extends Error {\n statusCode: number\n constructor(statusCode: number, message: string) {\n super(message)\n this.statusCode = statusCode\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["RouterContext", "jsx", "jsx", "RouterContext"]
7
+ }
@@ -0,0 +1,21 @@
1
+ export interface ApiRoute {
2
+ path: string;
3
+ key: string;
4
+ params: string[];
5
+ regex: RegExp;
6
+ }
7
+ export interface ApiMiddleware {
8
+ dir: string;
9
+ key: string;
10
+ }
11
+ export interface ApiResult {
12
+ routes: ApiRoute[];
13
+ middlewares: ApiMiddleware[];
14
+ }
15
+ export declare function invalidateApiCache(): void;
16
+ export declare function buildRoutes(routeKeys: string[], middlewareKeys: string[], apiDir: string): ApiResult;
17
+ export declare function collectMiddlewareChain(routeKey: string, middlewares: ApiMiddleware[]): ApiMiddleware[];
18
+ export declare function matchRoute(pathname: string, routes: ApiRoute[]): {
19
+ route: ApiRoute;
20
+ params: Record<string, string>;
21
+ } | null;
@@ -0,0 +1,64 @@
1
+ // src/utils/patterns.ts
2
+ function routePattern(rel) {
3
+ return rel.replace(/\.(tsx|ts|jsx|js)$/, "").replace(/\(.*?\)\//g, "").replace(/^index$|\/index$/, "").replace(/\[([^\]]+)]/g, ":$1") || "/";
4
+ }
5
+
6
+ // src/server/api-router.ts
7
+ function keyToRoutePattern(key, apiDir) {
8
+ const rel = key.slice(apiDir.length + 1).replace(/\\/g, "/");
9
+ const pattern = routePattern(rel);
10
+ return pattern === "/" ? "/api" : `/api/${pattern}`.replace("/api//", "/api/");
11
+ }
12
+ function keyToDir(key) {
13
+ return key.slice(0, key.lastIndexOf("/"));
14
+ }
15
+ var cache = null;
16
+ function invalidateApiCache() {
17
+ cache = null;
18
+ }
19
+ function buildRoutes(routeKeys, middlewareKeys, apiDir) {
20
+ if (cache) return cache;
21
+ const routes = [];
22
+ const middlewares = [];
23
+ for (const key of middlewareKeys) {
24
+ middlewares.push({ dir: keyToDir(key), key });
25
+ }
26
+ for (const key of routeKeys) {
27
+ const pattern = keyToRoutePattern(key, apiDir);
28
+ const params = [...pattern.matchAll(/:([^/]+)/g)].map((m) => m[1]);
29
+ const regexStr = pattern.replace(/:[^/]+/g, "([^/]+)").replace(/\//g, "\\/");
30
+ routes.push({ path: pattern, key, params, regex: new RegExp(`^${regexStr}$`) });
31
+ }
32
+ routes.sort((a, b) => {
33
+ const aScore = (a.path.match(/:/g) || []).length;
34
+ const bScore = (b.path.match(/:/g) || []).length;
35
+ if (aScore !== bScore) return aScore - bScore;
36
+ return b.path.length - a.path.length;
37
+ });
38
+ cache = { routes, middlewares };
39
+ return cache;
40
+ }
41
+ function collectMiddlewareChain(routeKey, middlewares) {
42
+ const routeDir = keyToDir(routeKey);
43
+ return middlewares.filter((mw) => routeDir.startsWith(mw.dir)).sort((a, b) => a.dir.split("/").length - b.dir.split("/").length);
44
+ }
45
+ function matchRoute(pathname, routes) {
46
+ for (const route of routes) {
47
+ const match = pathname.match(route.regex);
48
+ if (match) {
49
+ const params = {};
50
+ route.params.forEach((name, i) => {
51
+ params[name] = decodeURIComponent(match[i + 1]);
52
+ });
53
+ return { route, params };
54
+ }
55
+ }
56
+ return null;
57
+ }
58
+ export {
59
+ buildRoutes,
60
+ collectMiddlewareChain,
61
+ invalidateApiCache,
62
+ matchRoute
63
+ };
64
+ //# sourceMappingURL=api-router.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/patterns.ts", "../../src/server/api-router.ts"],
4
+ "sourcesContent": ["export function routePattern(rel: string): string {\n return rel\n .replace(/\\.(tsx|ts|jsx|js)$/, '')\n .replace(/\\(.*?\\)\\//g, '')\n .replace(/^index$|\\/index$/, '')\n .replace(/\\[([^\\]]+)]/g, ':$1')\n || '/'\n}", "import {routePattern} from \"../utils/patterns\";\n\nexport interface ApiRoute {\n path: string\n key: string\n params: string[]\n regex: RegExp\n}\n\nexport interface ApiMiddleware {\n dir: string\n key: string\n}\n\nexport interface ApiResult {\n routes: ApiRoute[]\n middlewares: ApiMiddleware[]\n}\n\nfunction keyToRoutePattern(key: string, apiDir: string): string {\n const rel = key.slice(apiDir.length + 1).replace(/\\\\/g, '/')\n const pattern = routePattern(rel)\n return pattern === '/' ? '/api' : `/api/${pattern}`.replace('/api//', '/api/')\n}\n\nfunction keyToDir(key: string): string {\n return key.slice(0, key.lastIndexOf('/'))\n}\n\nlet cache: ApiResult | null = null\n\nexport function invalidateApiCache() {\n cache = null\n}\n\nexport function buildRoutes(routeKeys: string[], middlewareKeys: string[], apiDir: string): ApiResult {\n if (cache) return cache\n\n const routes: ApiRoute[] = []\n const middlewares: ApiMiddleware[] = []\n\n for (const key of middlewareKeys) {\n middlewares.push({dir: keyToDir(key), key})\n }\n\n for (const key of routeKeys) {\n const pattern = keyToRoutePattern(key, apiDir)\n const params = [...pattern.matchAll(/:([^/]+)/g)].map(m => m[1])\n const regexStr = pattern\n .replace(/:[^/]+/g, '([^/]+)')\n .replace(/\\//g, '\\\\/')\n routes.push({path: pattern, key, params, regex: new RegExp(`^${regexStr}$`)})\n }\n routes.sort((a, b) => {\n const aScore = (a.path.match(/:/g) || []).length\n const bScore = (b.path.match(/:/g) || []).length\n if (aScore !== bScore) return aScore - bScore\n return b.path.length - a.path.length\n })\n\n cache = {routes, middlewares}\n return cache\n}\n\nexport function collectMiddlewareChain(routeKey: string, middlewares: ApiMiddleware[]): ApiMiddleware[] {\n const routeDir = keyToDir(routeKey)\n\n return middlewares\n .filter(mw => routeDir.startsWith(mw.dir))\n .sort((a, b) => a.dir.split('/').length - b.dir.split('/').length)\n}\n\nexport function matchRoute(\n pathname: string,\n routes: ApiRoute[]\n): {route: ApiRoute; params: Record<string, string>} | null {\n for (const route of routes) {\n const match = pathname.match(route.regex)\n if (match) {\n const params: Record<string, string> = {}\n route.params.forEach((name, i) => {\n params[name] = decodeURIComponent(match[i + 1])\n })\n return {route, params}\n }\n }\n return null\n}\n"],
5
+ "mappings": ";AAAO,SAAS,aAAa,KAAqB;AAC9C,SAAO,IACE,QAAQ,sBAAsB,EAAE,EAChC,QAAQ,cAAc,EAAE,EACxB,QAAQ,oBAAoB,EAAE,EAC9B,QAAQ,gBAAgB,KAAK,KAC/B;AACX;;;ACYA,SAAS,kBAAkB,KAAa,QAAwB;AAC5D,QAAM,MAAM,IAAI,MAAM,OAAO,SAAS,CAAC,EAAE,QAAQ,OAAO,GAAG;AAC3D,QAAM,UAAU,aAAa,GAAG;AAChC,SAAO,YAAY,MAAM,SAAS,QAAQ,OAAO,GAAG,QAAQ,UAAU,OAAO;AACjF;AAEA,SAAS,SAAS,KAAqB;AACnC,SAAO,IAAI,MAAM,GAAG,IAAI,YAAY,GAAG,CAAC;AAC5C;AAEA,IAAI,QAA0B;AAEvB,SAAS,qBAAqB;AACjC,UAAQ;AACZ;AAEO,SAAS,YAAY,WAAqB,gBAA0B,QAA2B;AAClG,MAAI,MAAO,QAAO;AAElB,QAAM,SAAqB,CAAC;AAC5B,QAAM,cAA+B,CAAC;AAEtC,aAAW,OAAO,gBAAgB;AAC9B,gBAAY,KAAK,EAAC,KAAK,SAAS,GAAG,GAAG,IAAG,CAAC;AAAA,EAC9C;AAEA,aAAW,OAAO,WAAW;AACzB,UAAM,UAAU,kBAAkB,KAAK,MAAM;AAC7C,UAAM,SAAS,CAAC,GAAG,QAAQ,SAAS,WAAW,CAAC,EAAE,IAAI,OAAK,EAAE,CAAC,CAAC;AAC/D,UAAM,WAAW,QACZ,QAAQ,WAAW,SAAS,EAC5B,QAAQ,OAAO,KAAK;AACzB,WAAO,KAAK,EAAC,MAAM,SAAS,KAAK,QAAQ,OAAO,IAAI,OAAO,IAAI,QAAQ,GAAG,EAAC,CAAC;AAAA,EAChF;AACA,SAAO,KAAK,CAAC,GAAG,MAAM;AAClB,UAAM,UAAU,EAAE,KAAK,MAAM,IAAI,KAAK,CAAC,GAAG;AAC1C,UAAM,UAAU,EAAE,KAAK,MAAM,IAAI,KAAK,CAAC,GAAG;AAC1C,QAAI,WAAW,OAAQ,QAAO,SAAS;AACvC,WAAO,EAAE,KAAK,SAAS,EAAE,KAAK;AAAA,EAClC,CAAC;AAED,UAAQ,EAAC,QAAQ,YAAW;AAC5B,SAAO;AACX;AAEO,SAAS,uBAAuB,UAAkB,aAA+C;AACpG,QAAM,WAAW,SAAS,QAAQ;AAElC,SAAO,YACF,OAAO,QAAM,SAAS,WAAW,GAAG,GAAG,CAAC,EACxC,KAAK,CAAC,GAAG,MAAM,EAAE,IAAI,MAAM,GAAG,EAAE,SAAS,EAAE,IAAI,MAAM,GAAG,EAAE,MAAM;AACzE;AAEO,SAAS,WACZ,UACA,QACwD;AACxD,aAAW,SAAS,QAAQ;AACxB,UAAM,QAAQ,SAAS,MAAM,MAAM,KAAK;AACxC,QAAI,OAAO;AACP,YAAM,SAAiC,CAAC;AACxC,YAAM,OAAO,QAAQ,CAAC,MAAM,MAAM;AAC9B,eAAO,IAAI,IAAI,mBAAmB,MAAM,IAAI,CAAC,CAAC;AAAA,MAClD,CAAC;AACD,aAAO,EAAC,OAAO,OAAM;AAAA,IACzB;AAAA,EACJ;AACA,SAAO;AACX;",
6
+ "names": []
7
+ }
@@ -0,0 +1,2 @@
1
+ import type { ApiGlob } from './types';
2
+ export declare function handleApiRequest(url: string, request: Request, glob: ApiGlob): Promise<Response>;
@@ -0,0 +1,123 @@
1
+ // src/utils/patterns.ts
2
+ function routePattern(rel) {
3
+ return rel.replace(/\.(tsx|ts|jsx|js)$/, "").replace(/\(.*?\)\//g, "").replace(/^index$|\/index$/, "").replace(/\[([^\]]+)]/g, ":$1") || "/";
4
+ }
5
+
6
+ // src/server/api-router.ts
7
+ function keyToRoutePattern(key, apiDir) {
8
+ const rel = key.slice(apiDir.length + 1).replace(/\\/g, "/");
9
+ const pattern = routePattern(rel);
10
+ return pattern === "/" ? "/api" : `/api/${pattern}`.replace("/api//", "/api/");
11
+ }
12
+ function keyToDir(key) {
13
+ return key.slice(0, key.lastIndexOf("/"));
14
+ }
15
+ var cache = null;
16
+ function buildRoutes(routeKeys, middlewareKeys, apiDir) {
17
+ if (cache) return cache;
18
+ const routes = [];
19
+ const middlewares = [];
20
+ for (const key of middlewareKeys) {
21
+ middlewares.push({ dir: keyToDir(key), key });
22
+ }
23
+ for (const key of routeKeys) {
24
+ const pattern = keyToRoutePattern(key, apiDir);
25
+ const params = [...pattern.matchAll(/:([^/]+)/g)].map((m) => m[1]);
26
+ const regexStr = pattern.replace(/:[^/]+/g, "([^/]+)").replace(/\//g, "\\/");
27
+ routes.push({ path: pattern, key, params, regex: new RegExp(`^${regexStr}$`) });
28
+ }
29
+ routes.sort((a, b) => {
30
+ const aScore = (a.path.match(/:/g) || []).length;
31
+ const bScore = (b.path.match(/:/g) || []).length;
32
+ if (aScore !== bScore) return aScore - bScore;
33
+ return b.path.length - a.path.length;
34
+ });
35
+ cache = { routes, middlewares };
36
+ return cache;
37
+ }
38
+ function collectMiddlewareChain(routeKey, middlewares) {
39
+ const routeDir = keyToDir(routeKey);
40
+ return middlewares.filter((mw) => routeDir.startsWith(mw.dir)).sort((a, b) => a.dir.split("/").length - b.dir.split("/").length);
41
+ }
42
+ function matchRoute(pathname, routes) {
43
+ for (const route of routes) {
44
+ const match = pathname.match(route.regex);
45
+ if (match) {
46
+ const params = {};
47
+ route.params.forEach((name, i) => {
48
+ params[name] = decodeURIComponent(match[i + 1]);
49
+ });
50
+ return { route, params };
51
+ }
52
+ }
53
+ return null;
54
+ }
55
+
56
+ // src/runtime/api-context.ts
57
+ var RouteContext = class {
58
+ params;
59
+ _state = /* @__PURE__ */ new Map();
60
+ constructor(params = {}) {
61
+ this.params = params;
62
+ }
63
+ set(key, value) {
64
+ this._state.set(key, value);
65
+ }
66
+ get(key) {
67
+ return this._state.get(key);
68
+ }
69
+ };
70
+
71
+ // src/runtime/error-boundary.tsx
72
+ import { Component } from "react";
73
+ import { jsx } from "react/jsx-runtime";
74
+ var DevixError = class extends Error {
75
+ statusCode;
76
+ constructor(statusCode, message) {
77
+ super(message);
78
+ this.statusCode = statusCode;
79
+ }
80
+ };
81
+
82
+ // src/server/api.ts
83
+ async function handleApiRequest(url, request, glob) {
84
+ try {
85
+ const { pathname } = new URL(url, "http://localhost");
86
+ const { routes, middlewares } = buildRoutes(
87
+ Object.keys(glob.routes),
88
+ Object.keys(glob.middlewares),
89
+ glob.apiDir
90
+ );
91
+ const matched = matchRoute(pathname, routes);
92
+ if (!matched) return new Response("Not Found", { status: 404 });
93
+ const { route, params } = matched;
94
+ const ctx = new RouteContext(params);
95
+ const middlewareChain = collectMiddlewareChain(route.key, middlewares);
96
+ for (const mw of middlewareChain) {
97
+ const mod2 = await glob.middlewares[mw.key]();
98
+ if (mod2.middleware) {
99
+ const result2 = await mod2.middleware(ctx, request);
100
+ if (result2 instanceof Response) return result2;
101
+ }
102
+ }
103
+ const mod = await glob.routes[route.key]();
104
+ const method = request.method.toUpperCase();
105
+ const handler = mod[method];
106
+ if (!handler) return new Response("Method Not Allowed", { status: 405 });
107
+ const result = await handler(ctx, request);
108
+ if (result instanceof Response) return result;
109
+ return new Response(JSON.stringify(result), {
110
+ headers: { "Content-Type": "application/json" }
111
+ });
112
+ } catch (err) {
113
+ console.error("[devix] api error:", err);
114
+ if (err instanceof DevixError) {
115
+ return new Response(err.message, { status: err.statusCode });
116
+ }
117
+ return new Response("Internal Server Error", { status: 500 });
118
+ }
119
+ }
120
+ export {
121
+ handleApiRequest
122
+ };
123
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/patterns.ts", "../../src/server/api-router.ts", "../../src/runtime/api-context.ts", "../../src/runtime/error-boundary.tsx", "../../src/server/api.ts"],
4
+ "sourcesContent": ["export function routePattern(rel: string): string {\n return rel\n .replace(/\\.(tsx|ts|jsx|js)$/, '')\n .replace(/\\(.*?\\)\\//g, '')\n .replace(/^index$|\\/index$/, '')\n .replace(/\\[([^\\]]+)]/g, ':$1')\n || '/'\n}", "import {routePattern} from \"../utils/patterns\";\n\nexport interface ApiRoute {\n path: string\n key: string\n params: string[]\n regex: RegExp\n}\n\nexport interface ApiMiddleware {\n dir: string\n key: string\n}\n\nexport interface ApiResult {\n routes: ApiRoute[]\n middlewares: ApiMiddleware[]\n}\n\nfunction keyToRoutePattern(key: string, apiDir: string): string {\n const rel = key.slice(apiDir.length + 1).replace(/\\\\/g, '/')\n const pattern = routePattern(rel)\n return pattern === '/' ? '/api' : `/api/${pattern}`.replace('/api//', '/api/')\n}\n\nfunction keyToDir(key: string): string {\n return key.slice(0, key.lastIndexOf('/'))\n}\n\nlet cache: ApiResult | null = null\n\nexport function invalidateApiCache() {\n cache = null\n}\n\nexport function buildRoutes(routeKeys: string[], middlewareKeys: string[], apiDir: string): ApiResult {\n if (cache) return cache\n\n const routes: ApiRoute[] = []\n const middlewares: ApiMiddleware[] = []\n\n for (const key of middlewareKeys) {\n middlewares.push({dir: keyToDir(key), key})\n }\n\n for (const key of routeKeys) {\n const pattern = keyToRoutePattern(key, apiDir)\n const params = [...pattern.matchAll(/:([^/]+)/g)].map(m => m[1])\n const regexStr = pattern\n .replace(/:[^/]+/g, '([^/]+)')\n .replace(/\\//g, '\\\\/')\n routes.push({path: pattern, key, params, regex: new RegExp(`^${regexStr}$`)})\n }\n routes.sort((a, b) => {\n const aScore = (a.path.match(/:/g) || []).length\n const bScore = (b.path.match(/:/g) || []).length\n if (aScore !== bScore) return aScore - bScore\n return b.path.length - a.path.length\n })\n\n cache = {routes, middlewares}\n return cache\n}\n\nexport function collectMiddlewareChain(routeKey: string, middlewares: ApiMiddleware[]): ApiMiddleware[] {\n const routeDir = keyToDir(routeKey)\n\n return middlewares\n .filter(mw => routeDir.startsWith(mw.dir))\n .sort((a, b) => a.dir.split('/').length - b.dir.split('/').length)\n}\n\nexport function matchRoute(\n pathname: string,\n routes: ApiRoute[]\n): {route: ApiRoute; params: Record<string, string>} | null {\n for (const route of routes) {\n const match = pathname.match(route.regex)\n if (match) {\n const params: Record<string, string> = {}\n route.params.forEach((name, i) => {\n params[name] = decodeURIComponent(match[i + 1])\n })\n return {route, params}\n }\n }\n return null\n}\n", "export class RouteContext {\n readonly params: Record<string, string>\n private _state = new Map<string, unknown>()\n\n constructor(params: Record<string, string> = {}) {\n this.params = params\n }\n\n set<T>(key: string, value: T): void {\n this._state.set(key, value)\n }\n\n get<T>(key: string): T | undefined {\n return this._state.get(key) as T\n }\n}\n\nexport type RouteHandler = (ctx: RouteContext, request: Request) => Promise<Response | null> | Response | null\n\nexport interface MiddlewareModule {\n middleware: (ctx: RouteContext, request: Request) => Promise<Response | null> | Response | null\n}\n\nexport interface RouteModule {\n GET?: RouteHandler\n POST?: RouteHandler\n PUT?: RouteHandler\n PATCH?: RouteHandler\n DELETE?: RouteHandler\n HEAD?: RouteHandler\n OPTIONS?: RouteHandler\n}", "import {Component, ComponentType, ReactNode} from \"react\";\nimport {ErrorProps} from \"../server/types\";\n\ninterface Props {\n ErrorPage?: ComponentType<ErrorProps>\n children: ReactNode\n}\n\ninterface State {\n error: ErrorProps | null\n}\n\nexport class DevixErrorBoundary extends Component<Props, State> {\n state: State = { error: null }\n\n static getDerivedStateFromError(err: unknown): State {\n if (err instanceof DevixError) {\n return {\n error: {statusCode: err.statusCode, message: err.message}\n }\n }\n return {\n error: {statusCode: 500, message: err instanceof Error ? err.message : 'Unknown error'}\n }\n }\n\n render() {\n if (this.state.error && this.props.ErrorPage) {\n return <this.props.ErrorPage {...this.state.error} />\n }\n if (this.state.error) {\n return <h1>{this.state.error.statusCode}</h1>\n }\n return this.props.children\n }\n}\n\nexport class DevixError extends Error {\n statusCode: number\n constructor(statusCode: number, message: string) {\n super(message)\n this.statusCode = statusCode\n }\n}\n", "import {buildRoutes, matchRoute, collectMiddlewareChain} from './api-router'\nimport {RouteContext} from '../runtime/api-context'\nimport type {RouteModule, MiddlewareModule} from '../runtime/api-context'\nimport type {ApiGlob} from './types'\nimport {DevixError} from '../runtime/error-boundary'\n\nexport async function handleApiRequest(\n url: string,\n request: Request,\n glob: ApiGlob,\n): Promise<Response> {\n try {\n const {pathname} = new URL(url, 'http://localhost')\n const {routes, middlewares} = buildRoutes(\n Object.keys(glob.routes),\n Object.keys(glob.middlewares),\n glob.apiDir,\n )\n const matched = matchRoute(pathname, routes)\n\n if (!matched) return new Response('Not Found', {status: 404})\n\n const {route, params} = matched\n const ctx = new RouteContext(params)\n\n const middlewareChain = collectMiddlewareChain(route.key, middlewares)\n for (const mw of middlewareChain) {\n const mod = await glob.middlewares[mw.key]() as MiddlewareModule\n if (mod.middleware) {\n const result = await mod.middleware(ctx, request)\n if (result instanceof Response) return result\n }\n }\n\n const mod = await glob.routes[route.key]() as RouteModule\n const method = request.method.toUpperCase() as keyof RouteModule\n const handler = mod[method]\n\n if (!handler) return new Response('Method Not Allowed', {status: 405})\n\n const result = await handler(ctx, request)\n if (result instanceof Response) return result\n\n return new Response(JSON.stringify(result), {\n headers: {'Content-Type': 'application/json'},\n })\n } catch (err) {\n console.error('[devix] api error:', err)\n if (err instanceof DevixError) {\n return new Response(err.message, {status: err.statusCode})\n }\n return new Response('Internal Server Error', {status: 500})\n }\n}"],
5
+ "mappings": "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",
6
+ "names": ["mod", "result"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import type { ViteDevServer } from 'vite';
2
+ export declare function collectCss(vite: ViteDevServer): Promise<string[]>;
@@ -0,0 +1,15 @@
1
+ // src/server/collect-css.ts
2
+ async function collectCss(vite) {
3
+ const cssUrls = /* @__PURE__ */ new Set();
4
+ for (const [, mod] of vite.moduleGraph.idToModuleMap) {
5
+ if (!mod.id) continue;
6
+ if (mod.id.endsWith(".css") || mod.id.includes(".css?")) {
7
+ cssUrls.add(mod.url);
8
+ }
9
+ }
10
+ return [...cssUrls];
11
+ }
12
+ export {
13
+ collectCss
14
+ };
15
+ //# sourceMappingURL=collect-css.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/server/collect-css.ts"],
4
+ "sourcesContent": ["import type {ViteDevServer} from 'vite'\n\nexport async function collectCss(vite: ViteDevServer): Promise<string[]> {\n const cssUrls = new Set<string>()\n\n for (const [, mod] of vite.moduleGraph.idToModuleMap) {\n if (!mod.id) continue\n if (mod.id.endsWith('.css') || mod.id.includes('.css?')) {\n cssUrls.add(mod.url)\n }\n }\n\n return [...cssUrls]\n}"],
5
+ "mappings": ";AAEA,eAAsB,WAAW,MAAwC;AACrE,QAAM,UAAU,oBAAI,IAAY;AAEhC,aAAW,CAAC,EAAE,GAAG,KAAK,KAAK,YAAY,eAAe;AAClD,QAAI,CAAC,IAAI,GAAI;AACb,QAAI,IAAI,GAAG,SAAS,MAAM,KAAK,IAAI,GAAG,SAAS,OAAO,GAAG;AACrD,cAAQ,IAAI,IAAI,GAAG;AAAA,IACvB;AAAA,EACJ;AAEA,SAAO,CAAC,GAAG,OAAO;AACtB;",
6
+ "names": []
7
+ }
@@ -0,0 +1,6 @@
1
+ export { buildPages, collectLayoutChain, matchPage } from './pages-router';
2
+ export { buildRoutes, collectMiddlewareChain, matchRoute } from './api-router';
3
+ export { resolveMetadata, mergeMetadata } from '../runtime/metadata';
4
+ export type { Page, Layout, PagesResult } from './pages-router';
5
+ export type { ApiRoute, ApiMiddleware, ApiResult } from './api-router';
6
+ export type { PageModule, LayoutModule } from './types';