@guren/server 1.0.0 → 1.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.
- package/dist/{chunk-VXXZIXAP.js → chunk-QH4NUKSV.js} +4 -4
- package/dist/index.js +662 -660
- package/dist/runtime/index.js +1 -1
- package/dist/vite/index.d.ts +2 -2
- package/dist/vite/index.js +5 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
logDevServerBanner,
|
|
3
3
|
parseImportMap,
|
|
4
4
|
startViteDevServer
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-QH4NUKSV.js";
|
|
6
6
|
import {
|
|
7
7
|
PendingSchedule,
|
|
8
8
|
Schedule,
|
|
@@ -1706,644 +1706,729 @@ var ErrorServiceProvider = class extends ServiceProvider {
|
|
|
1706
1706
|
}
|
|
1707
1707
|
};
|
|
1708
1708
|
|
|
1709
|
-
// src/
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1709
|
+
// src/mvc/inertia/InertiaEngine.ts
|
|
1710
|
+
import { isAbsolute, resolve as resolvePath } from "path";
|
|
1711
|
+
import { pathToFileURL } from "url";
|
|
1712
|
+
ensureErrorStackTracePolyfill();
|
|
1713
|
+
var DEFAULT_TITLE = "Guren";
|
|
1714
|
+
var DEV_FALLBACK_IMPORT_MAP = {
|
|
1715
|
+
react: "https://esm.sh/react@19.0.0?dev",
|
|
1716
|
+
"react/jsx-runtime": "https://esm.sh/react@19.0.0/jsx-runtime?dev",
|
|
1717
|
+
"react/jsx-dev-runtime": "https://esm.sh/react@19.0.0/jsx-dev-runtime?dev",
|
|
1718
|
+
"react-dom/client": "https://esm.sh/react-dom@19.0.0/client?dev",
|
|
1719
|
+
"@guren/inertia-client": "/vendor/inertia-client.tsx",
|
|
1720
|
+
"@inertiajs/react": "https://esm.sh/@inertiajs/react@2.2.15?dev&external=react,react-dom/client"
|
|
1721
|
+
};
|
|
1722
|
+
async function inertia(component, props, options = {}) {
|
|
1723
|
+
const resolvedVersion = options.version ?? process.env.GUREN_INERTIA_VERSION ?? void 0;
|
|
1724
|
+
const page = {
|
|
1725
|
+
component,
|
|
1726
|
+
props,
|
|
1727
|
+
url: options.url ?? "",
|
|
1728
|
+
version: resolvedVersion
|
|
1729
|
+
};
|
|
1730
|
+
const request = options.request;
|
|
1731
|
+
const isInertiaVisit = Boolean(request?.headers.get("X-Inertia"));
|
|
1732
|
+
const prefersJson = request ? acceptsJson(request) : false;
|
|
1733
|
+
const versionHeader = resolvedVersion ? { "X-Inertia-Version": resolvedVersion } : {};
|
|
1734
|
+
if (request && resolvedVersion && isInertiaVisit && request.method === "GET") {
|
|
1735
|
+
const clientVersion = request.headers.get("X-Inertia-Version");
|
|
1736
|
+
if (clientVersion !== resolvedVersion) {
|
|
1737
|
+
return new Response(null, {
|
|
1738
|
+
status: 409,
|
|
1739
|
+
headers: {
|
|
1740
|
+
"X-Inertia-Location": options.url ?? request.url,
|
|
1741
|
+
Vary: "Accept"
|
|
1742
|
+
}
|
|
1743
|
+
});
|
|
1744
|
+
}
|
|
1715
1745
|
}
|
|
1716
|
-
if (
|
|
1717
|
-
|
|
1746
|
+
if (isInertiaVisit || prefersJson) {
|
|
1747
|
+
return new Response(serializePage(page), {
|
|
1748
|
+
status: options.status ?? 200,
|
|
1749
|
+
headers: {
|
|
1750
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
1751
|
+
"X-Inertia": "true",
|
|
1752
|
+
Vary: "Accept",
|
|
1753
|
+
...versionHeader,
|
|
1754
|
+
...options.headers
|
|
1755
|
+
}
|
|
1756
|
+
});
|
|
1718
1757
|
}
|
|
1719
|
-
|
|
1758
|
+
const html = await renderDocument(page, options);
|
|
1759
|
+
return new Response(html, {
|
|
1760
|
+
status: options.status ?? 200,
|
|
1761
|
+
headers: {
|
|
1762
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
1763
|
+
"X-Inertia": "true",
|
|
1764
|
+
Vary: "Accept",
|
|
1765
|
+
...versionHeader,
|
|
1766
|
+
...options.headers
|
|
1767
|
+
}
|
|
1768
|
+
});
|
|
1720
1769
|
}
|
|
1721
|
-
function
|
|
1722
|
-
|
|
1770
|
+
async function renderDocument(page, options) {
|
|
1771
|
+
const defaultEntry = process.env.GUREN_INERTIA_ENTRY ?? "/resources/js/app.tsx";
|
|
1772
|
+
const entry = options.entry ?? defaultEntry;
|
|
1773
|
+
const title = escapeHtml(options.title ?? DEFAULT_TITLE);
|
|
1774
|
+
const styles = options.styles ?? parseStylesEnv(process.env.GUREN_INERTIA_STYLES);
|
|
1775
|
+
const envImportMap = parseImportMap(process.env.GUREN_INERTIA_IMPORT_MAP, {
|
|
1776
|
+
context: "GUREN_INERTIA_IMPORT_MAP"
|
|
1777
|
+
});
|
|
1778
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
1779
|
+
const importMapEntries = {
|
|
1780
|
+
...isProduction ? {} : DEV_FALLBACK_IMPORT_MAP,
|
|
1781
|
+
...envImportMap,
|
|
1782
|
+
...options.importMap ?? {}
|
|
1783
|
+
};
|
|
1784
|
+
const importMap = JSON.stringify({ imports: importMapEntries }, null, 2);
|
|
1785
|
+
const serializedPage = serializePage(page);
|
|
1786
|
+
const stylesheetLinks = renderStyles(styles);
|
|
1787
|
+
const docsHeadBootstrap = renderDocsHeadBootstrap(page.component);
|
|
1788
|
+
const ssrResult = await tryRenderSsr(page, options);
|
|
1789
|
+
const headElements = (ssrResult?.head ?? []).map(normalizeHeadElement);
|
|
1790
|
+
const hasCustomTitle = headElements.some(
|
|
1791
|
+
(element) => /<title\b[^>]*>/iu.test(element)
|
|
1792
|
+
);
|
|
1793
|
+
const headSegments = [
|
|
1794
|
+
'<meta charset="utf-8" />',
|
|
1795
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1" />',
|
|
1796
|
+
hasCustomTitle ? "" : `<title>${title}</title>`,
|
|
1797
|
+
docsHeadBootstrap,
|
|
1798
|
+
stylesheetLinks,
|
|
1799
|
+
...headElements,
|
|
1800
|
+
Object.keys(importMapEntries).length > 0 ? `<script type="importmap">${importMap}</script>` : "",
|
|
1801
|
+
`<script>window.__INERTIA_PAGE__ = ${serializedPage};</script>`
|
|
1802
|
+
].filter((segment) => segment && segment.length > 0);
|
|
1803
|
+
const appMarkup = ssrResult?.body ?? `<script data-page="app" type="application/json">${serializedPage}</script><div id="app"></div>`;
|
|
1804
|
+
const bodyClass = resolveBodyClass(page.component);
|
|
1805
|
+
const bodyAttributes = bodyClass ? ` class="${escapeAttribute(bodyClass)}"` : "";
|
|
1806
|
+
return `<!DOCTYPE html>
|
|
1807
|
+
<html lang="en">
|
|
1808
|
+
<head>
|
|
1809
|
+
${headSegments.join("\n ")}
|
|
1810
|
+
</head>
|
|
1811
|
+
<body${bodyAttributes}>
|
|
1812
|
+
${appMarkup}
|
|
1813
|
+
<script type="module" src="${entry}"></script>
|
|
1814
|
+
</body>
|
|
1815
|
+
</html>`;
|
|
1723
1816
|
}
|
|
1724
|
-
function
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
const nextEntry = normalizeDevEntryUrl(devServerUrl);
|
|
1729
|
-
const currentEntry = process.env.GUREN_INERTIA_ENTRY;
|
|
1730
|
-
if (!currentEntry || currentEntry.endsWith(DEFAULT_DEV_ENTRY_PATH)) {
|
|
1731
|
-
process.env.GUREN_INERTIA_ENTRY = nextEntry;
|
|
1817
|
+
async function tryRenderSsr(page, options) {
|
|
1818
|
+
const ssrOptions = options.ssr;
|
|
1819
|
+
if (ssrOptions?.enabled === false) {
|
|
1820
|
+
return void 0;
|
|
1732
1821
|
}
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
const previous = state.__gurenActiveServer;
|
|
1740
|
-
if (!previous?.stop) {
|
|
1741
|
-
state.__gurenActiveServer = void 0;
|
|
1742
|
-
return;
|
|
1822
|
+
const manifest = (ssrOptions?.manifest ?? process.env.GUREN_INERTIA_SSR_MANIFEST)?.trim() || void 0;
|
|
1823
|
+
const renderer = ssrOptions?.render ?? await loadSsrRenderer(
|
|
1824
|
+
ssrOptions?.entry ?? process.env.GUREN_INERTIA_SSR_ENTRY
|
|
1825
|
+
);
|
|
1826
|
+
if (!renderer) {
|
|
1827
|
+
return void 0;
|
|
1743
1828
|
}
|
|
1744
1829
|
try {
|
|
1745
|
-
await
|
|
1830
|
+
const result = await renderer({
|
|
1831
|
+
page,
|
|
1832
|
+
request: options.request,
|
|
1833
|
+
manifest
|
|
1834
|
+
});
|
|
1835
|
+
if (!result || typeof result.body !== "string") {
|
|
1836
|
+
return void 0;
|
|
1837
|
+
}
|
|
1838
|
+
return {
|
|
1839
|
+
body: result.body,
|
|
1840
|
+
head: Array.isArray(result.head) ? result.head : []
|
|
1841
|
+
};
|
|
1746
1842
|
} catch (error) {
|
|
1747
|
-
console.
|
|
1748
|
-
|
|
1749
|
-
|
|
1843
|
+
console.error(
|
|
1844
|
+
"Inertia SSR renderer failed; falling back to client rendering.",
|
|
1845
|
+
error
|
|
1846
|
+
);
|
|
1847
|
+
return void 0;
|
|
1750
1848
|
}
|
|
1751
1849
|
}
|
|
1752
|
-
function
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
const state = getGlobalState();
|
|
1757
|
-
const previous = state.__gurenActiveViteDevServer;
|
|
1758
|
-
if (!previous) {
|
|
1759
|
-
state.__gurenActiveViteDevServer = void 0;
|
|
1760
|
-
clearManagedViteEnv();
|
|
1761
|
-
return;
|
|
1850
|
+
async function loadSsrRenderer(entry) {
|
|
1851
|
+
const specifier = entry?.trim();
|
|
1852
|
+
if (!specifier) {
|
|
1853
|
+
return void 0;
|
|
1762
1854
|
}
|
|
1855
|
+
const normalized = normalizeSsrSpecifier(specifier);
|
|
1763
1856
|
try {
|
|
1764
|
-
await
|
|
1857
|
+
const module = await import(normalized);
|
|
1858
|
+
const renderCandidate = extractSsrRenderer(module);
|
|
1859
|
+
if (!renderCandidate) {
|
|
1860
|
+
console.warn(
|
|
1861
|
+
`Inertia SSR entry "${specifier}" does not export a renderer. Expected a default export or a named "render" function.`
|
|
1862
|
+
);
|
|
1863
|
+
return void 0;
|
|
1864
|
+
}
|
|
1865
|
+
return renderCandidate;
|
|
1765
1866
|
} catch (error) {
|
|
1766
|
-
console.
|
|
1767
|
-
|
|
1768
|
-
state.__gurenActiveViteDevServer = void 0;
|
|
1769
|
-
clearManagedViteEnv();
|
|
1867
|
+
console.error(`Failed to import Inertia SSR entry "${specifier}".`, error);
|
|
1868
|
+
return void 0;
|
|
1770
1869
|
}
|
|
1771
1870
|
}
|
|
1772
|
-
function
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
var Application = class {
|
|
1776
|
-
constructor(options = {}) {
|
|
1777
|
-
this.options = options;
|
|
1778
|
-
this.hono = new Hono();
|
|
1779
|
-
this.container = new Container();
|
|
1780
|
-
this.router = new Router();
|
|
1781
|
-
this.authManager = new AuthManager();
|
|
1782
|
-
this.providerManager = new ProviderManager(this.container);
|
|
1783
|
-
this.container.instance("app", this);
|
|
1784
|
-
this.container.instance("hono", this.hono);
|
|
1785
|
-
this.container.instance("auth", this.authManager);
|
|
1786
|
-
this.container.instance("router", this.router);
|
|
1787
|
-
if (!this.authManager.guardNames().length) {
|
|
1788
|
-
this.authManager.registerGuard("web", ({ ctx, session, manager }) => {
|
|
1789
|
-
let provider;
|
|
1790
|
-
try {
|
|
1791
|
-
provider = manager.getProvider("users");
|
|
1792
|
-
} catch {
|
|
1793
|
-
provider = { retrieveById: async () => null, retrieveByCredentials: async () => null, validateCredentials: async () => false };
|
|
1794
|
-
}
|
|
1795
|
-
return new SessionGuard({ provider, session });
|
|
1796
|
-
});
|
|
1797
|
-
this.authManager.setDefaultGuard("web");
|
|
1798
|
-
}
|
|
1799
|
-
if (this.options.auth) {
|
|
1800
|
-
this.providerManager.register(AuthServiceProvider);
|
|
1801
|
-
} else {
|
|
1802
|
-
this.hono.use("*", attachAuthContext((ctx) => this.authManager.createAuthContext(ctx)));
|
|
1803
|
-
}
|
|
1804
|
-
this.providerManager.register(AuthorizationServiceProvider);
|
|
1805
|
-
const userProviders = Array.isArray(this.options.providers) ? this.options.providers : [];
|
|
1806
|
-
const hasUserErrorProvider = userProviders.some(
|
|
1807
|
-
(provider) => provider === ErrorServiceProvider || provider.prototype instanceof ErrorServiceProvider
|
|
1808
|
-
);
|
|
1809
|
-
if (!hasUserErrorProvider) {
|
|
1810
|
-
this.providerManager.register(ErrorServiceProvider);
|
|
1811
|
-
}
|
|
1812
|
-
if (userProviders.length > 0) {
|
|
1813
|
-
this.providerManager.registerMany(userProviders);
|
|
1814
|
-
}
|
|
1871
|
+
function extractSsrRenderer(module) {
|
|
1872
|
+
if (!module || typeof module !== "object") {
|
|
1873
|
+
return void 0;
|
|
1815
1874
|
}
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
providerManager;
|
|
1820
|
-
authManager;
|
|
1821
|
-
viteDevServer;
|
|
1822
|
-
bunServer;
|
|
1823
|
-
viteTeardownRegistered = false;
|
|
1824
|
-
bunTeardownRegistered = false;
|
|
1825
|
-
autoSessionAttached = false;
|
|
1826
|
-
routesRegistered = false;
|
|
1827
|
-
get auth() {
|
|
1828
|
-
return this.authManager;
|
|
1875
|
+
const candidate = typeof module.render === "function" ? module.render : typeof module.default === "function" ? module.default : void 0;
|
|
1876
|
+
if (!candidate) {
|
|
1877
|
+
return void 0;
|
|
1829
1878
|
}
|
|
1830
|
-
|
|
1831
|
-
|
|
1879
|
+
return (context) => Promise.resolve(candidate(context));
|
|
1880
|
+
}
|
|
1881
|
+
function normalizeSsrSpecifier(specifier) {
|
|
1882
|
+
if (specifier.startsWith("file://") || isUrlLike(specifier)) {
|
|
1883
|
+
return specifier;
|
|
1832
1884
|
}
|
|
1833
|
-
|
|
1834
|
-
|
|
1885
|
+
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
1886
|
+
const absolute = resolvePath(process.cwd(), specifier);
|
|
1887
|
+
return pathToFileURL(absolute).href;
|
|
1835
1888
|
}
|
|
1836
|
-
|
|
1837
|
-
return
|
|
1889
|
+
if (isAbsolute(specifier)) {
|
|
1890
|
+
return pathToFileURL(specifier).href;
|
|
1838
1891
|
}
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1892
|
+
return specifier;
|
|
1893
|
+
}
|
|
1894
|
+
function isUrlLike(specifier) {
|
|
1895
|
+
return /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(specifier);
|
|
1896
|
+
}
|
|
1897
|
+
function renderStyles(styles) {
|
|
1898
|
+
if (!styles.length) {
|
|
1899
|
+
return "";
|
|
1900
|
+
}
|
|
1901
|
+
return styles.map((href) => `<link rel="stylesheet" href="${escapeAttribute(href)}" />`).join("\n ");
|
|
1902
|
+
}
|
|
1903
|
+
function normalizeHeadElement(element) {
|
|
1904
|
+
const markup = typeof element === "string" ? element : String(element ?? "");
|
|
1905
|
+
const pattern = /href="\/(?!public\/)([^"?]+\.(?:js|css))(\?[^"']*)?"/g;
|
|
1906
|
+
return markup.replace(
|
|
1907
|
+
pattern,
|
|
1908
|
+
(_match, file2, query = "") => `href="/public/assets/${file2}${query}"`
|
|
1909
|
+
);
|
|
1910
|
+
}
|
|
1911
|
+
function resolveBodyClass(componentName) {
|
|
1912
|
+
return componentName.startsWith("Docs/") ? "docs-theme" : "";
|
|
1913
|
+
}
|
|
1914
|
+
function renderDocsHeadBootstrap(componentName) {
|
|
1915
|
+
if (!componentName.startsWith("Docs/")) {
|
|
1916
|
+
return "";
|
|
1917
|
+
}
|
|
1918
|
+
const criticalStyles = [
|
|
1919
|
+
"html,body{background:#ffffff;color:#1f2937;}",
|
|
1920
|
+
"body.docs-theme{background:#ffffff;color:#1f2937;}",
|
|
1921
|
+
"html.dark,html.dark body,html.dark body.docs-theme{background:#1a1a2e;color:#e0def4;}",
|
|
1922
|
+
".size-4{width:1rem;height:1rem;}",
|
|
1923
|
+
".size-5{width:1.25rem;height:1.25rem;}",
|
|
1924
|
+
".size-6{width:1.5rem;height:1.5rem;}",
|
|
1925
|
+
".size-8{width:2rem;height:2rem;}",
|
|
1926
|
+
".size-9{width:2.25rem;height:2.25rem;}"
|
|
1927
|
+
].join("");
|
|
1928
|
+
const prepaintScript = [
|
|
1929
|
+
"(function(){",
|
|
1930
|
+
"try{",
|
|
1931
|
+
"var mode=localStorage.getItem('guren-color-mode')||'system';",
|
|
1932
|
+
"var dark=mode==='dark'||(mode!=='light'&&window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches);",
|
|
1933
|
+
"var root=document.documentElement;",
|
|
1934
|
+
"root.classList.toggle('dark',dark);",
|
|
1935
|
+
"root.style.colorScheme=dark?'dark':'light';",
|
|
1936
|
+
"}catch(_){",
|
|
1937
|
+
"}",
|
|
1938
|
+
"})();"
|
|
1939
|
+
].join("");
|
|
1940
|
+
return `<style id="guren-docs-critical">${criticalStyles}</style><script>${prepaintScript}</script>`;
|
|
1941
|
+
}
|
|
1942
|
+
function serializePage(page) {
|
|
1943
|
+
return JSON.stringify(page).replace(/[<\u2028\u2029]/gu, (char) => {
|
|
1944
|
+
switch (char) {
|
|
1945
|
+
case "<":
|
|
1946
|
+
return "\\u003c";
|
|
1947
|
+
case "\u2028":
|
|
1948
|
+
return "\\u2028";
|
|
1949
|
+
case "\u2029":
|
|
1950
|
+
return "\\u2029";
|
|
1951
|
+
default:
|
|
1952
|
+
return char;
|
|
1846
1953
|
}
|
|
1847
|
-
|
|
1954
|
+
});
|
|
1955
|
+
}
|
|
1956
|
+
function acceptsJson(request) {
|
|
1957
|
+
const accept = request.headers.get("accept")?.toLowerCase() ?? "";
|
|
1958
|
+
if (!accept || accept === "*/*") {
|
|
1959
|
+
return false;
|
|
1848
1960
|
}
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
*/
|
|
1852
|
-
use(path, ...middleware) {
|
|
1853
|
-
this.hono.use(path, ...middleware);
|
|
1961
|
+
if (accept.includes("text/html")) {
|
|
1962
|
+
return false;
|
|
1854
1963
|
}
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1964
|
+
return accept.includes("application/json") || accept.includes("json");
|
|
1965
|
+
}
|
|
1966
|
+
function escapeHtml(value) {
|
|
1967
|
+
return value.replace(/[&<>"']/gu, (char) => {
|
|
1968
|
+
switch (char) {
|
|
1969
|
+
case "&":
|
|
1970
|
+
return "&";
|
|
1971
|
+
case "<":
|
|
1972
|
+
return "<";
|
|
1973
|
+
case ">":
|
|
1974
|
+
return ">";
|
|
1975
|
+
case '"':
|
|
1976
|
+
return """;
|
|
1977
|
+
case "'":
|
|
1978
|
+
return "'";
|
|
1979
|
+
default:
|
|
1980
|
+
return char;
|
|
1981
|
+
}
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1984
|
+
function escapeAttribute(value) {
|
|
1985
|
+
return value.replace(/[&"]/gu, (char) => {
|
|
1986
|
+
if (char === "&") {
|
|
1987
|
+
return "&";
|
|
1988
|
+
}
|
|
1989
|
+
return """;
|
|
1990
|
+
});
|
|
1991
|
+
}
|
|
1992
|
+
function parseStylesEnv(value) {
|
|
1993
|
+
if (!value) {
|
|
1994
|
+
return [];
|
|
1865
1995
|
}
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1996
|
+
return value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
// src/mvc/ViewEngine.ts
|
|
2000
|
+
var ViewEngine = class {
|
|
2001
|
+
static engines = /* @__PURE__ */ new Map();
|
|
2002
|
+
static register(name, renderer) {
|
|
2003
|
+
this.engines.set(name, renderer);
|
|
2004
|
+
}
|
|
2005
|
+
static has(name) {
|
|
2006
|
+
return this.engines.has(name);
|
|
2007
|
+
}
|
|
2008
|
+
static render(name, template, props) {
|
|
2009
|
+
const engine = this.engines.get(name);
|
|
2010
|
+
if (!engine) {
|
|
2011
|
+
throw new Error(`View engine "${name}" has not been registered.`);
|
|
1873
2012
|
}
|
|
1874
|
-
|
|
2013
|
+
return engine(template, props);
|
|
1875
2014
|
}
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
2015
|
+
};
|
|
2016
|
+
|
|
2017
|
+
// src/mvc/inertia/shared.ts
|
|
2018
|
+
var resolver = null;
|
|
2019
|
+
function setInertiaSharedProps(resolverFn) {
|
|
2020
|
+
resolver = resolverFn;
|
|
2021
|
+
}
|
|
2022
|
+
function getInertiaSharedPropsResolver() {
|
|
2023
|
+
return resolver;
|
|
2024
|
+
}
|
|
2025
|
+
function shareInertiaProps(resolverFn) {
|
|
2026
|
+
const previous = resolver;
|
|
2027
|
+
resolver = async (ctx) => {
|
|
2028
|
+
const prev = previous ? await previous(ctx) : {};
|
|
2029
|
+
const next = await resolverFn(ctx);
|
|
2030
|
+
return { ...prev, ...next };
|
|
2031
|
+
};
|
|
2032
|
+
}
|
|
2033
|
+
async function resolveSharedInertiaProps(ctx) {
|
|
2034
|
+
if (!resolver) return {};
|
|
2035
|
+
const shared = await resolver(ctx);
|
|
2036
|
+
if (shared && typeof shared === "object") {
|
|
2037
|
+
return shared;
|
|
2038
|
+
}
|
|
2039
|
+
return {};
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
// src/providers/InertiaServiceProvider.ts
|
|
2043
|
+
var InertiaServiceProvider = class extends ServiceProvider {
|
|
2044
|
+
register() {
|
|
2045
|
+
if (!ViewEngine.has("inertia")) {
|
|
2046
|
+
ViewEngine.register("inertia", inertia);
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
boot() {
|
|
2050
|
+
this.registerValidationRenderer();
|
|
2051
|
+
this.registerSharedErrors();
|
|
1880
2052
|
}
|
|
1881
2053
|
/**
|
|
1882
|
-
*
|
|
1883
|
-
*
|
|
1884
|
-
* This allows AI coding agents to introspect the project.
|
|
2054
|
+
* Register a custom renderer for ValidationException on Inertia requests.
|
|
2055
|
+
* Non-Inertia requests fall through to the default JSON 422 response.
|
|
1885
2056
|
*/
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
return;
|
|
1889
|
-
}
|
|
2057
|
+
registerValidationRenderer() {
|
|
2058
|
+
let handler;
|
|
1890
2059
|
try {
|
|
1891
|
-
|
|
1892
|
-
const provider = new McpServiceProvider(this.container);
|
|
1893
|
-
provider.register();
|
|
1894
|
-
await provider.boot();
|
|
2060
|
+
handler = this.container.make("exception.handler");
|
|
1895
2061
|
} catch {
|
|
2062
|
+
return;
|
|
1896
2063
|
}
|
|
2064
|
+
handler.render(ValidationException, (error, ctx) => {
|
|
2065
|
+
const isInertia = ctx.req.header("X-Inertia");
|
|
2066
|
+
if (!isInertia) {
|
|
2067
|
+
return ctx.json(
|
|
2068
|
+
{
|
|
2069
|
+
message: error.message,
|
|
2070
|
+
errors: error.errors
|
|
2071
|
+
},
|
|
2072
|
+
422
|
|
2073
|
+
);
|
|
2074
|
+
}
|
|
2075
|
+
const session = getSessionFromContext(ctx);
|
|
2076
|
+
if (session) {
|
|
2077
|
+
const flattened = {};
|
|
2078
|
+
for (const [key, messages] of Object.entries(error.errors ?? {})) {
|
|
2079
|
+
flattened[key] = messages[0] ?? "";
|
|
2080
|
+
}
|
|
2081
|
+
session.flash("errors", flattened);
|
|
2082
|
+
}
|
|
2083
|
+
const referer = ctx.req.header("Referer") ?? "/";
|
|
2084
|
+
return new Response(null, {
|
|
2085
|
+
status: 303,
|
|
2086
|
+
headers: { Location: referer }
|
|
2087
|
+
});
|
|
2088
|
+
});
|
|
1897
2089
|
}
|
|
1898
2090
|
/**
|
|
1899
|
-
*
|
|
2091
|
+
* Wrap existing shared props resolver to auto-inject flashed `errors`.
|
|
1900
2092
|
*/
|
|
1901
|
-
|
|
1902
|
-
|
|
2093
|
+
registerSharedErrors() {
|
|
2094
|
+
const previous = getInertiaSharedPropsResolver();
|
|
2095
|
+
setInertiaSharedProps(async (ctx) => {
|
|
2096
|
+
const prev = previous ? await previous(ctx) : {};
|
|
2097
|
+
const session = getSessionFromContext(ctx);
|
|
2098
|
+
const errors = session?.getFlash("errors");
|
|
2099
|
+
return {
|
|
2100
|
+
...prev,
|
|
2101
|
+
...errors && Object.keys(errors).length > 0 ? { errors } : {}
|
|
2102
|
+
};
|
|
2103
|
+
});
|
|
1903
2104
|
}
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
2105
|
+
};
|
|
2106
|
+
|
|
2107
|
+
// src/http/Application.ts
|
|
2108
|
+
var MANAGED_VITE_ENV_FLAG = "GUREN_MANAGED_VITE_DEV_SERVER";
|
|
2109
|
+
var DEFAULT_DEV_ENTRY_PATH = "/resources/js/dev-entry.ts";
|
|
2110
|
+
function clearManagedViteEnv() {
|
|
2111
|
+
if (typeof process === "undefined") {
|
|
2112
|
+
return;
|
|
2113
|
+
}
|
|
2114
|
+
if (process.env[MANAGED_VITE_ENV_FLAG] === "1") {
|
|
2115
|
+
delete process.env.VITE_DEV_SERVER_URL;
|
|
2116
|
+
}
|
|
2117
|
+
delete process.env[MANAGED_VITE_ENV_FLAG];
|
|
2118
|
+
}
|
|
2119
|
+
function normalizeDevEntryUrl(baseUrl) {
|
|
2120
|
+
return `${baseUrl.replace(/\/$/u, "")}${DEFAULT_DEV_ENTRY_PATH}`;
|
|
2121
|
+
}
|
|
2122
|
+
function syncManagedInertiaDevEntry(devServerUrl) {
|
|
2123
|
+
if (typeof process === "undefined") {
|
|
2124
|
+
return;
|
|
2125
|
+
}
|
|
2126
|
+
const nextEntry = normalizeDevEntryUrl(devServerUrl);
|
|
2127
|
+
const currentEntry = process.env.GUREN_INERTIA_ENTRY;
|
|
2128
|
+
if (!currentEntry || currentEntry.endsWith(DEFAULT_DEV_ENTRY_PATH)) {
|
|
2129
|
+
process.env.GUREN_INERTIA_ENTRY = nextEntry;
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
function getGlobalState() {
|
|
2133
|
+
return globalThis;
|
|
2134
|
+
}
|
|
2135
|
+
async function stopActiveBunServer() {
|
|
2136
|
+
const state = getGlobalState();
|
|
2137
|
+
const previous = state.__gurenActiveServer;
|
|
2138
|
+
if (!previous?.stop) {
|
|
2139
|
+
state.__gurenActiveServer = void 0;
|
|
2140
|
+
return;
|
|
2141
|
+
}
|
|
2142
|
+
try {
|
|
2143
|
+
await Promise.resolve(previous.stop());
|
|
2144
|
+
} catch (error) {
|
|
2145
|
+
console.warn("Failed to stop previous Bun server:", error);
|
|
2146
|
+
} finally {
|
|
2147
|
+
state.__gurenActiveServer = void 0;
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
function setActiveBunServer(server) {
|
|
2151
|
+
getGlobalState().__gurenActiveServer = server;
|
|
2152
|
+
}
|
|
2153
|
+
async function stopActiveViteDevServer() {
|
|
2154
|
+
const state = getGlobalState();
|
|
2155
|
+
const previous = state.__gurenActiveViteDevServer;
|
|
2156
|
+
if (!previous) {
|
|
2157
|
+
state.__gurenActiveViteDevServer = void 0;
|
|
2158
|
+
clearManagedViteEnv();
|
|
2159
|
+
return;
|
|
2160
|
+
}
|
|
2161
|
+
try {
|
|
2162
|
+
await previous.close();
|
|
2163
|
+
} catch (error) {
|
|
2164
|
+
console.warn("Failed to stop previous Vite dev server:", error);
|
|
2165
|
+
} finally {
|
|
2166
|
+
state.__gurenActiveViteDevServer = void 0;
|
|
2167
|
+
clearManagedViteEnv();
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
function setActiveViteDevServer(server) {
|
|
2171
|
+
getGlobalState().__gurenActiveViteDevServer = server;
|
|
2172
|
+
}
|
|
2173
|
+
var Application = class {
|
|
2174
|
+
constructor(options = {}) {
|
|
2175
|
+
this.options = options;
|
|
2176
|
+
this.hono = new Hono();
|
|
2177
|
+
this.container = new Container();
|
|
2178
|
+
this.router = new Router();
|
|
2179
|
+
this.authManager = new AuthManager();
|
|
2180
|
+
this.providerManager = new ProviderManager(this.container);
|
|
2181
|
+
this.container.instance("app", this);
|
|
2182
|
+
this.container.instance("hono", this.hono);
|
|
2183
|
+
this.container.instance("auth", this.authManager);
|
|
2184
|
+
this.container.instance("router", this.router);
|
|
2185
|
+
if (!this.authManager.guardNames().length) {
|
|
2186
|
+
this.authManager.registerGuard("web", ({ ctx, session, manager }) => {
|
|
2187
|
+
let provider;
|
|
2188
|
+
try {
|
|
2189
|
+
provider = manager.getProvider("users");
|
|
2190
|
+
} catch {
|
|
2191
|
+
provider = { retrieveById: async () => null, retrieveByCredentials: async () => null, validateCredentials: async () => false };
|
|
1933
2192
|
}
|
|
1934
|
-
|
|
1935
|
-
this.registerViteTeardown();
|
|
1936
|
-
} catch (error) {
|
|
1937
|
-
console.error("Failed to start Vite dev server:", error);
|
|
1938
|
-
process.exit(1);
|
|
1939
|
-
}
|
|
1940
|
-
}
|
|
1941
|
-
const server = Bun.serve({
|
|
1942
|
-
port,
|
|
1943
|
-
hostname,
|
|
1944
|
-
fetch: (request) => this.fetch(request)
|
|
1945
|
-
});
|
|
1946
|
-
this.bunServer = server;
|
|
1947
|
-
setActiveBunServer(server);
|
|
1948
|
-
this.registerBunTeardown();
|
|
1949
|
-
const shouldLogBanner = typeof process === "undefined" || process.env?.NODE_ENV !== "production" && process.env?.GUREN_DEV_BANNER !== "0";
|
|
1950
|
-
if (shouldLogBanner) {
|
|
1951
|
-
this.logDevServerBanner({
|
|
1952
|
-
hostname,
|
|
1953
|
-
port,
|
|
1954
|
-
assetsUrl: resolvedAssetsUrl ?? "http://localhost:5173"
|
|
2193
|
+
return new SessionGuard({ provider, session });
|
|
1955
2194
|
});
|
|
2195
|
+
this.authManager.setDefaultGuard("web");
|
|
2196
|
+
}
|
|
2197
|
+
if (this.options.auth) {
|
|
2198
|
+
this.providerManager.register(AuthServiceProvider);
|
|
2199
|
+
} else {
|
|
2200
|
+
this.hono.use("*", attachAuthContext((ctx) => this.authManager.createAuthContext(ctx)));
|
|
2201
|
+
}
|
|
2202
|
+
this.providerManager.register(AuthorizationServiceProvider);
|
|
2203
|
+
const userProviders = Array.isArray(this.options.providers) ? this.options.providers : [];
|
|
2204
|
+
const hasUserErrorProvider = userProviders.some(
|
|
2205
|
+
(provider) => provider === ErrorServiceProvider || provider.prototype instanceof ErrorServiceProvider
|
|
2206
|
+
);
|
|
2207
|
+
if (!hasUserErrorProvider) {
|
|
2208
|
+
this.providerManager.register(ErrorServiceProvider);
|
|
2209
|
+
}
|
|
2210
|
+
if (userProviders.length > 0) {
|
|
2211
|
+
this.providerManager.registerMany(userProviders);
|
|
2212
|
+
}
|
|
2213
|
+
const hasUserInertiaProvider = userProviders.some(
|
|
2214
|
+
(provider) => provider === InertiaServiceProvider || provider.prototype instanceof InertiaServiceProvider
|
|
2215
|
+
);
|
|
2216
|
+
if (!hasUserInertiaProvider) {
|
|
2217
|
+
this.providerManager.register(InertiaServiceProvider);
|
|
1956
2218
|
}
|
|
1957
2219
|
}
|
|
2220
|
+
hono;
|
|
2221
|
+
container;
|
|
2222
|
+
router;
|
|
2223
|
+
providerManager;
|
|
2224
|
+
authManager;
|
|
2225
|
+
viteDevServer;
|
|
2226
|
+
bunServer;
|
|
2227
|
+
viteTeardownRegistered = false;
|
|
2228
|
+
bunTeardownRegistered = false;
|
|
2229
|
+
autoSessionAttached = false;
|
|
2230
|
+
routesRegistered = false;
|
|
2231
|
+
get auth() {
|
|
2232
|
+
return this.authManager;
|
|
2233
|
+
}
|
|
2234
|
+
get authOptions() {
|
|
2235
|
+
return this.options.auth;
|
|
2236
|
+
}
|
|
2237
|
+
markAutoSessionAttached() {
|
|
2238
|
+
this.autoSessionAttached = true;
|
|
2239
|
+
}
|
|
2240
|
+
hasAutoSessionAttached() {
|
|
2241
|
+
return this.autoSessionAttached;
|
|
2242
|
+
}
|
|
1958
2243
|
/**
|
|
1959
|
-
*
|
|
2244
|
+
* Mounts the application router onto the Hono instance.
|
|
1960
2245
|
*/
|
|
1961
|
-
|
|
1962
|
-
this.
|
|
1963
|
-
|
|
2246
|
+
async mountRoutes() {
|
|
2247
|
+
if (this.options.routes && !this.routesRegistered) {
|
|
2248
|
+
await this.options.routes(this.router);
|
|
2249
|
+
this.routesRegistered = true;
|
|
2250
|
+
}
|
|
2251
|
+
this.router.mount(this.hono, { container: this.container });
|
|
1964
2252
|
}
|
|
1965
2253
|
/**
|
|
1966
|
-
*
|
|
2254
|
+
* Allows registering global middlewares directly on the underlying Hono app.
|
|
1967
2255
|
*/
|
|
1968
|
-
|
|
1969
|
-
this.
|
|
1970
|
-
return this;
|
|
2256
|
+
use(path, ...middleware) {
|
|
2257
|
+
this.hono.use(path, ...middleware);
|
|
1971
2258
|
}
|
|
1972
2259
|
/**
|
|
1973
|
-
*
|
|
2260
|
+
* Executes provider registration, boot callback, mounts routes, and boots providers.
|
|
1974
2261
|
*/
|
|
1975
|
-
|
|
1976
|
-
|
|
2262
|
+
async boot() {
|
|
2263
|
+
this.mountSecurityDefaults();
|
|
2264
|
+
await this.providerManager.registerAll();
|
|
2265
|
+
await this.options.boot?.(this.hono);
|
|
2266
|
+
await this.mountRoutes();
|
|
2267
|
+
await this.mountMcpEndpoint();
|
|
2268
|
+
await this.providerManager.bootAll();
|
|
1977
2269
|
}
|
|
1978
|
-
|
|
1979
|
-
|
|
2270
|
+
/**
|
|
2271
|
+
* Registers default security middleware (headers + host authorization).
|
|
2272
|
+
*/
|
|
2273
|
+
mountSecurityDefaults() {
|
|
2274
|
+
const { securityHeaders } = this.options;
|
|
2275
|
+
if (securityHeaders !== false) {
|
|
2276
|
+
this.hono.use("*", createSecurityHeaders(securityHeaders ?? {}));
|
|
2277
|
+
}
|
|
2278
|
+
this.mountHostAuthorization();
|
|
2279
|
+
}
|
|
2280
|
+
mountHostAuthorization() {
|
|
2281
|
+
const { hostAuthorization } = this.options;
|
|
2282
|
+
if (hostAuthorization === false || !hostAuthorization) return;
|
|
2283
|
+
this.hono.use("*", createHostAuthorizationMiddleware(hostAuthorization));
|
|
2284
|
+
}
|
|
2285
|
+
/**
|
|
2286
|
+
* Mounts the MCP endpoint at /_guren/mcp when explicitly enabled.
|
|
2287
|
+
* Requires GUREN_MCP=1 environment variable. Never active in production.
|
|
2288
|
+
* This allows AI coding agents to introspect the project.
|
|
2289
|
+
*/
|
|
2290
|
+
async mountMcpEndpoint() {
|
|
2291
|
+
if (typeof process === "undefined" || process.env?.NODE_ENV === "production" || process.env?.GUREN_MCP !== "1") {
|
|
1980
2292
|
return;
|
|
1981
2293
|
}
|
|
1982
2294
|
try {
|
|
1983
|
-
await
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
setActiveViteDevServer();
|
|
1989
|
-
}
|
|
1990
|
-
this.viteDevServer = void 0;
|
|
1991
|
-
this.viteTeardownRegistered = false;
|
|
1992
|
-
clearManagedViteEnv();
|
|
2295
|
+
const { McpServiceProvider } = await import("./McpServiceProvider-JW6PDVMD.js");
|
|
2296
|
+
const provider = new McpServiceProvider(this.container);
|
|
2297
|
+
provider.register();
|
|
2298
|
+
await provider.boot();
|
|
2299
|
+
} catch {
|
|
1993
2300
|
}
|
|
1994
2301
|
}
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
this.
|
|
2000
|
-
const exitHandler = () => {
|
|
2001
|
-
this.closeViteDevServer().then(() => process.exit(0)).catch(() => process.exit(1));
|
|
2002
|
-
};
|
|
2003
|
-
process.once("SIGINT", exitHandler);
|
|
2004
|
-
process.once("SIGTERM", exitHandler);
|
|
2005
|
-
process.on("exit", () => {
|
|
2006
|
-
if (this.viteDevServer) {
|
|
2007
|
-
void this.viteDevServer.close();
|
|
2008
|
-
}
|
|
2009
|
-
});
|
|
2010
|
-
}
|
|
2011
|
-
registerBunTeardown() {
|
|
2012
|
-
if (this.bunTeardownRegistered || typeof process === "undefined") {
|
|
2013
|
-
return;
|
|
2014
|
-
}
|
|
2015
|
-
this.bunTeardownRegistered = true;
|
|
2016
|
-
const exitHandler = () => {
|
|
2017
|
-
stopActiveBunServer().then(() => process.exit(0)).catch(() => process.exit(1));
|
|
2018
|
-
};
|
|
2019
|
-
process.once("SIGINT", exitHandler);
|
|
2020
|
-
process.once("SIGTERM", exitHandler);
|
|
2021
|
-
process.on("exit", () => {
|
|
2022
|
-
void stopActiveBunServer();
|
|
2023
|
-
});
|
|
2024
|
-
}
|
|
2025
|
-
};
|
|
2026
|
-
function createApp(options = {}) {
|
|
2027
|
-
return new Application(options);
|
|
2028
|
-
}
|
|
2029
|
-
|
|
2030
|
-
// src/mvc/inertia/InertiaEngine.ts
|
|
2031
|
-
import { isAbsolute, resolve as resolvePath } from "path";
|
|
2032
|
-
import { pathToFileURL } from "url";
|
|
2033
|
-
ensureErrorStackTracePolyfill();
|
|
2034
|
-
var DEFAULT_TITLE = "Guren";
|
|
2035
|
-
var DEFAULT_IMPORT_MAP = {
|
|
2036
|
-
react: "https://esm.sh/react@19.0.0?dev",
|
|
2037
|
-
"react/jsx-runtime": "https://esm.sh/react@19.0.0/jsx-runtime?dev",
|
|
2038
|
-
"react/jsx-dev-runtime": "https://esm.sh/react@19.0.0/jsx-dev-runtime?dev",
|
|
2039
|
-
"react-dom/client": "https://esm.sh/react-dom@19.0.0/client?dev",
|
|
2040
|
-
"@guren/inertia-client": "/vendor/inertia-client.tsx",
|
|
2041
|
-
"@inertiajs/react": "https://esm.sh/@inertiajs/react@2.2.15?dev&external=react,react-dom/client"
|
|
2042
|
-
};
|
|
2043
|
-
async function inertia(component, props, options = {}) {
|
|
2044
|
-
const resolvedVersion = options.version ?? process.env.GUREN_INERTIA_VERSION ?? void 0;
|
|
2045
|
-
const page = {
|
|
2046
|
-
component,
|
|
2047
|
-
props,
|
|
2048
|
-
url: options.url ?? "",
|
|
2049
|
-
version: resolvedVersion
|
|
2050
|
-
};
|
|
2051
|
-
const request = options.request;
|
|
2052
|
-
const isInertiaVisit = Boolean(request?.headers.get("X-Inertia"));
|
|
2053
|
-
const prefersJson = request ? acceptsJson(request) : false;
|
|
2054
|
-
const versionHeader = resolvedVersion ? { "X-Inertia-Version": resolvedVersion } : {};
|
|
2055
|
-
if (request && resolvedVersion && isInertiaVisit && request.method === "GET") {
|
|
2056
|
-
const clientVersion = request.headers.get("X-Inertia-Version");
|
|
2057
|
-
if (clientVersion !== resolvedVersion) {
|
|
2058
|
-
return new Response(null, {
|
|
2059
|
-
status: 409,
|
|
2060
|
-
headers: {
|
|
2061
|
-
"X-Inertia-Location": options.url ?? request.url,
|
|
2062
|
-
Vary: "Accept"
|
|
2063
|
-
}
|
|
2064
|
-
});
|
|
2065
|
-
}
|
|
2066
|
-
}
|
|
2067
|
-
if (isInertiaVisit || prefersJson) {
|
|
2068
|
-
return new Response(serializePage(page), {
|
|
2069
|
-
status: options.status ?? 200,
|
|
2070
|
-
headers: {
|
|
2071
|
-
"Content-Type": "application/json; charset=utf-8",
|
|
2072
|
-
"X-Inertia": "true",
|
|
2073
|
-
Vary: "Accept",
|
|
2074
|
-
...versionHeader,
|
|
2075
|
-
...options.headers
|
|
2076
|
-
}
|
|
2077
|
-
});
|
|
2078
|
-
}
|
|
2079
|
-
const html = await renderDocument(page, options);
|
|
2080
|
-
return new Response(html, {
|
|
2081
|
-
status: options.status ?? 200,
|
|
2082
|
-
headers: {
|
|
2083
|
-
"Content-Type": "text/html; charset=utf-8",
|
|
2084
|
-
"X-Inertia": "true",
|
|
2085
|
-
Vary: "Accept",
|
|
2086
|
-
...versionHeader,
|
|
2087
|
-
...options.headers
|
|
2088
|
-
}
|
|
2089
|
-
});
|
|
2090
|
-
}
|
|
2091
|
-
async function renderDocument(page, options) {
|
|
2092
|
-
const defaultEntry = process.env.GUREN_INERTIA_ENTRY ?? "/resources/js/app.tsx";
|
|
2093
|
-
const entry = options.entry ?? defaultEntry;
|
|
2094
|
-
const title = escapeHtml(options.title ?? DEFAULT_TITLE);
|
|
2095
|
-
const styles = options.styles ?? parseStylesEnv(process.env.GUREN_INERTIA_STYLES);
|
|
2096
|
-
const envImportMap = parseImportMap(process.env.GUREN_INERTIA_IMPORT_MAP, {
|
|
2097
|
-
context: "GUREN_INERTIA_IMPORT_MAP"
|
|
2098
|
-
});
|
|
2099
|
-
const importMap = JSON.stringify(
|
|
2100
|
-
{
|
|
2101
|
-
imports: {
|
|
2102
|
-
...DEFAULT_IMPORT_MAP,
|
|
2103
|
-
...envImportMap,
|
|
2104
|
-
...options.importMap ?? {}
|
|
2105
|
-
}
|
|
2106
|
-
},
|
|
2107
|
-
null,
|
|
2108
|
-
2
|
|
2109
|
-
);
|
|
2110
|
-
const serializedPage = serializePage(page);
|
|
2111
|
-
const stylesheetLinks = renderStyles(styles);
|
|
2112
|
-
const docsHeadBootstrap = renderDocsHeadBootstrap(page.component);
|
|
2113
|
-
const ssrResult = await tryRenderSsr(page, options);
|
|
2114
|
-
const headElements = (ssrResult?.head ?? []).map(normalizeHeadElement);
|
|
2115
|
-
const hasCustomTitle = headElements.some(
|
|
2116
|
-
(element) => /<title\b[^>]*>/iu.test(element)
|
|
2117
|
-
);
|
|
2118
|
-
const headSegments = [
|
|
2119
|
-
'<meta charset="utf-8" />',
|
|
2120
|
-
'<meta name="viewport" content="width=device-width, initial-scale=1" />',
|
|
2121
|
-
hasCustomTitle ? "" : `<title>${title}</title>`,
|
|
2122
|
-
docsHeadBootstrap,
|
|
2123
|
-
stylesheetLinks,
|
|
2124
|
-
...headElements,
|
|
2125
|
-
`<script type="importmap">${importMap}</script>`,
|
|
2126
|
-
`<script>window.__INERTIA_PAGE__ = ${serializedPage};</script>`
|
|
2127
|
-
].filter((segment) => segment && segment.length > 0);
|
|
2128
|
-
const appMarkup = ssrResult?.body ?? `<script data-page="app" type="application/json">${serializedPage}</script><div id="app"></div>`;
|
|
2129
|
-
const bodyClass = resolveBodyClass(page.component);
|
|
2130
|
-
const bodyAttributes = bodyClass ? ` class="${escapeAttribute(bodyClass)}"` : "";
|
|
2131
|
-
return `<!DOCTYPE html>
|
|
2132
|
-
<html lang="en">
|
|
2133
|
-
<head>
|
|
2134
|
-
${headSegments.join("\n ")}
|
|
2135
|
-
</head>
|
|
2136
|
-
<body${bodyAttributes}>
|
|
2137
|
-
${appMarkup}
|
|
2138
|
-
<script type="module" src="${entry}"></script>
|
|
2139
|
-
</body>
|
|
2140
|
-
</html>`;
|
|
2141
|
-
}
|
|
2142
|
-
async function tryRenderSsr(page, options) {
|
|
2143
|
-
const ssrOptions = options.ssr;
|
|
2144
|
-
if (ssrOptions?.enabled === false) {
|
|
2145
|
-
return void 0;
|
|
2146
|
-
}
|
|
2147
|
-
const manifest = (ssrOptions?.manifest ?? process.env.GUREN_INERTIA_SSR_MANIFEST)?.trim() || void 0;
|
|
2148
|
-
const renderer = ssrOptions?.render ?? await loadSsrRenderer(
|
|
2149
|
-
ssrOptions?.entry ?? process.env.GUREN_INERTIA_SSR_ENTRY
|
|
2150
|
-
);
|
|
2151
|
-
if (!renderer) {
|
|
2152
|
-
return void 0;
|
|
2153
|
-
}
|
|
2154
|
-
try {
|
|
2155
|
-
const result = await renderer({
|
|
2156
|
-
page,
|
|
2157
|
-
request: options.request,
|
|
2158
|
-
manifest
|
|
2159
|
-
});
|
|
2160
|
-
if (!result || typeof result.body !== "string") {
|
|
2161
|
-
return void 0;
|
|
2162
|
-
}
|
|
2163
|
-
return {
|
|
2164
|
-
body: result.body,
|
|
2165
|
-
head: Array.isArray(result.head) ? result.head : []
|
|
2166
|
-
};
|
|
2167
|
-
} catch (error) {
|
|
2168
|
-
console.error(
|
|
2169
|
-
"Inertia SSR renderer failed; falling back to client rendering.",
|
|
2170
|
-
error
|
|
2171
|
-
);
|
|
2172
|
-
return void 0;
|
|
2173
|
-
}
|
|
2174
|
-
}
|
|
2175
|
-
async function loadSsrRenderer(entry) {
|
|
2176
|
-
const specifier = entry?.trim();
|
|
2177
|
-
if (!specifier) {
|
|
2178
|
-
return void 0;
|
|
2179
|
-
}
|
|
2180
|
-
const normalized = normalizeSsrSpecifier(specifier);
|
|
2181
|
-
try {
|
|
2182
|
-
const module = await import(normalized);
|
|
2183
|
-
const renderCandidate = extractSsrRenderer(module);
|
|
2184
|
-
if (!renderCandidate) {
|
|
2185
|
-
console.warn(
|
|
2186
|
-
`Inertia SSR entry "${specifier}" does not export a renderer. Expected a default export or a named "render" function.`
|
|
2187
|
-
);
|
|
2188
|
-
return void 0;
|
|
2189
|
-
}
|
|
2190
|
-
return renderCandidate;
|
|
2191
|
-
} catch (error) {
|
|
2192
|
-
console.error(`Failed to import Inertia SSR entry "${specifier}".`, error);
|
|
2193
|
-
return void 0;
|
|
2194
|
-
}
|
|
2195
|
-
}
|
|
2196
|
-
function extractSsrRenderer(module) {
|
|
2197
|
-
if (!module || typeof module !== "object") {
|
|
2198
|
-
return void 0;
|
|
2199
|
-
}
|
|
2200
|
-
const candidate = typeof module.render === "function" ? module.render : typeof module.default === "function" ? module.default : void 0;
|
|
2201
|
-
if (!candidate) {
|
|
2202
|
-
return void 0;
|
|
2203
|
-
}
|
|
2204
|
-
return (context) => Promise.resolve(candidate(context));
|
|
2205
|
-
}
|
|
2206
|
-
function normalizeSsrSpecifier(specifier) {
|
|
2207
|
-
if (specifier.startsWith("file://") || isUrlLike(specifier)) {
|
|
2208
|
-
return specifier;
|
|
2209
|
-
}
|
|
2210
|
-
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
2211
|
-
const absolute = resolvePath(process.cwd(), specifier);
|
|
2212
|
-
return pathToFileURL(absolute).href;
|
|
2213
|
-
}
|
|
2214
|
-
if (isAbsolute(specifier)) {
|
|
2215
|
-
return pathToFileURL(specifier).href;
|
|
2216
|
-
}
|
|
2217
|
-
return specifier;
|
|
2218
|
-
}
|
|
2219
|
-
function isUrlLike(specifier) {
|
|
2220
|
-
return /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(specifier);
|
|
2221
|
-
}
|
|
2222
|
-
function renderStyles(styles) {
|
|
2223
|
-
if (!styles.length) {
|
|
2224
|
-
return "";
|
|
2225
|
-
}
|
|
2226
|
-
return styles.map((href) => `<link rel="stylesheet" href="${escapeAttribute(href)}" />`).join("\n ");
|
|
2227
|
-
}
|
|
2228
|
-
function normalizeHeadElement(element) {
|
|
2229
|
-
const markup = typeof element === "string" ? element : String(element ?? "");
|
|
2230
|
-
const pattern = /href="\/(?!public\/)([^"?]+\.(?:js|css))(\?[^"']*)?"/g;
|
|
2231
|
-
return markup.replace(
|
|
2232
|
-
pattern,
|
|
2233
|
-
(_match, file2, query = "") => `href="/public/assets/${file2}${query}"`
|
|
2234
|
-
);
|
|
2235
|
-
}
|
|
2236
|
-
function resolveBodyClass(componentName) {
|
|
2237
|
-
return componentName.startsWith("Docs/") ? "docs-theme" : "";
|
|
2238
|
-
}
|
|
2239
|
-
function renderDocsHeadBootstrap(componentName) {
|
|
2240
|
-
if (!componentName.startsWith("Docs/")) {
|
|
2241
|
-
return "";
|
|
2302
|
+
/**
|
|
2303
|
+
* Fetch handler to integrate with Bun.serve or any standard Fetch runtime.
|
|
2304
|
+
*/
|
|
2305
|
+
async fetch(request, env, executionCtx) {
|
|
2306
|
+
return this.hono.fetch(request, env, executionCtx);
|
|
2242
2307
|
}
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
"
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2308
|
+
/**
|
|
2309
|
+
* Convenience helper to start a Bun server when available.
|
|
2310
|
+
*/
|
|
2311
|
+
async listen(options = {}) {
|
|
2312
|
+
if (!Bun) {
|
|
2313
|
+
throw new Error("Bun runtime is required to call Application.listen");
|
|
2314
|
+
}
|
|
2315
|
+
await stopActiveBunServer();
|
|
2316
|
+
await stopActiveViteDevServer();
|
|
2317
|
+
const { port = 3e3, hostname = "0.0.0.0", assetsUrl, vite } = options;
|
|
2318
|
+
const externalAssetsUrl = typeof process !== "undefined" && process.env?.[MANAGED_VITE_ENV_FLAG] !== "1" ? process.env?.VITE_DEV_SERVER_URL : void 0;
|
|
2319
|
+
let resolvedAssetsUrl = assetsUrl ?? externalAssetsUrl;
|
|
2320
|
+
const shouldStartVite = vite !== false && typeof process !== "undefined" && process.env?.NODE_ENV !== "production" && !resolvedAssetsUrl && process.env?.GUREN_DEV_VITE !== "0";
|
|
2321
|
+
if (shouldStartVite) {
|
|
2322
|
+
const viteOptions = typeof vite === "object" ? vite : void 0;
|
|
2323
|
+
try {
|
|
2324
|
+
await this.closeViteDevServer();
|
|
2325
|
+
const { server: server2, localUrl } = await startViteDevServer({
|
|
2326
|
+
root: viteOptions?.root ?? process.cwd(),
|
|
2327
|
+
config: viteOptions?.config,
|
|
2328
|
+
host: viteOptions?.host ?? true,
|
|
2329
|
+
port: viteOptions?.port
|
|
2330
|
+
});
|
|
2331
|
+
this.viteDevServer = server2;
|
|
2332
|
+
setActiveViteDevServer(server2);
|
|
2333
|
+
resolvedAssetsUrl = localUrl;
|
|
2334
|
+
if (typeof process !== "undefined") {
|
|
2335
|
+
process.env.VITE_DEV_SERVER_URL = resolvedAssetsUrl;
|
|
2336
|
+
process.env[MANAGED_VITE_ENV_FLAG] = "1";
|
|
2337
|
+
}
|
|
2338
|
+
syncManagedInertiaDevEntry(resolvedAssetsUrl);
|
|
2339
|
+
this.registerViteTeardown();
|
|
2340
|
+
} catch (error) {
|
|
2341
|
+
console.error("Failed to start Vite dev server:", error);
|
|
2342
|
+
process.exit(1);
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
const server = Bun.serve({
|
|
2346
|
+
port,
|
|
2347
|
+
hostname,
|
|
2348
|
+
fetch: (request) => this.fetch(request)
|
|
2349
|
+
});
|
|
2350
|
+
this.bunServer = server;
|
|
2351
|
+
setActiveBunServer(server);
|
|
2352
|
+
this.registerBunTeardown();
|
|
2353
|
+
const shouldLogBanner = typeof process === "undefined" || process.env?.NODE_ENV !== "production" && process.env?.GUREN_DEV_BANNER !== "0";
|
|
2354
|
+
if (shouldLogBanner) {
|
|
2355
|
+
this.logDevServerBanner({
|
|
2356
|
+
hostname,
|
|
2357
|
+
port,
|
|
2358
|
+
assetsUrl: resolvedAssetsUrl ?? "http://localhost:5173"
|
|
2359
|
+
});
|
|
2278
2360
|
}
|
|
2279
|
-
});
|
|
2280
|
-
}
|
|
2281
|
-
function acceptsJson(request) {
|
|
2282
|
-
const accept = request.headers.get("accept")?.toLowerCase() ?? "";
|
|
2283
|
-
if (!accept || accept === "*/*") {
|
|
2284
|
-
return false;
|
|
2285
2361
|
}
|
|
2286
|
-
|
|
2287
|
-
|
|
2362
|
+
/**
|
|
2363
|
+
* Register a service provider.
|
|
2364
|
+
*/
|
|
2365
|
+
register(provider) {
|
|
2366
|
+
this.providerManager.register(provider);
|
|
2367
|
+
return this;
|
|
2288
2368
|
}
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
return char;
|
|
2369
|
+
/**
|
|
2370
|
+
* Register multiple service providers.
|
|
2371
|
+
*/
|
|
2372
|
+
registerMany(providers) {
|
|
2373
|
+
this.providerManager.registerMany(providers);
|
|
2374
|
+
return this;
|
|
2375
|
+
}
|
|
2376
|
+
/**
|
|
2377
|
+
* Logs the rich development server banner to the console.
|
|
2378
|
+
*/
|
|
2379
|
+
logDevServerBanner(options) {
|
|
2380
|
+
logDevServerBanner(options);
|
|
2381
|
+
}
|
|
2382
|
+
async closeViteDevServer() {
|
|
2383
|
+
if (!this.viteDevServer) {
|
|
2384
|
+
return;
|
|
2306
2385
|
}
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2386
|
+
try {
|
|
2387
|
+
await this.viteDevServer.close();
|
|
2388
|
+
} catch (error) {
|
|
2389
|
+
console.error("Error while shutting down Vite dev server:", error);
|
|
2390
|
+
} finally {
|
|
2391
|
+
if (getGlobalState().__gurenActiveViteDevServer === this.viteDevServer) {
|
|
2392
|
+
setActiveViteDevServer();
|
|
2393
|
+
}
|
|
2394
|
+
this.viteDevServer = void 0;
|
|
2395
|
+
this.viteTeardownRegistered = false;
|
|
2396
|
+
clearManagedViteEnv();
|
|
2313
2397
|
}
|
|
2314
|
-
return """;
|
|
2315
|
-
});
|
|
2316
|
-
}
|
|
2317
|
-
function parseStylesEnv(value) {
|
|
2318
|
-
if (!value) {
|
|
2319
|
-
return [];
|
|
2320
2398
|
}
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
}
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
const next = await resolverFn(ctx);
|
|
2337
|
-
return { ...prev, ...next };
|
|
2338
|
-
};
|
|
2339
|
-
}
|
|
2340
|
-
async function resolveSharedInertiaProps(ctx) {
|
|
2341
|
-
if (!resolver) return {};
|
|
2342
|
-
const shared = await resolver(ctx);
|
|
2343
|
-
if (shared && typeof shared === "object") {
|
|
2344
|
-
return shared;
|
|
2399
|
+
registerViteTeardown() {
|
|
2400
|
+
if (this.viteTeardownRegistered || !this.viteDevServer || typeof process === "undefined") {
|
|
2401
|
+
return;
|
|
2402
|
+
}
|
|
2403
|
+
this.viteTeardownRegistered = true;
|
|
2404
|
+
const exitHandler = () => {
|
|
2405
|
+
this.closeViteDevServer().then(() => process.exit(0)).catch(() => process.exit(1));
|
|
2406
|
+
};
|
|
2407
|
+
process.once("SIGINT", exitHandler);
|
|
2408
|
+
process.once("SIGTERM", exitHandler);
|
|
2409
|
+
process.on("exit", () => {
|
|
2410
|
+
if (this.viteDevServer) {
|
|
2411
|
+
void this.viteDevServer.close();
|
|
2412
|
+
}
|
|
2413
|
+
});
|
|
2345
2414
|
}
|
|
2346
|
-
|
|
2415
|
+
registerBunTeardown() {
|
|
2416
|
+
if (this.bunTeardownRegistered || typeof process === "undefined") {
|
|
2417
|
+
return;
|
|
2418
|
+
}
|
|
2419
|
+
this.bunTeardownRegistered = true;
|
|
2420
|
+
const exitHandler = () => {
|
|
2421
|
+
stopActiveBunServer().then(() => process.exit(0)).catch(() => process.exit(1));
|
|
2422
|
+
};
|
|
2423
|
+
process.once("SIGINT", exitHandler);
|
|
2424
|
+
process.once("SIGTERM", exitHandler);
|
|
2425
|
+
process.on("exit", () => {
|
|
2426
|
+
void stopActiveBunServer();
|
|
2427
|
+
});
|
|
2428
|
+
}
|
|
2429
|
+
};
|
|
2430
|
+
function createApp(options = {}) {
|
|
2431
|
+
return new Application(options);
|
|
2347
2432
|
}
|
|
2348
2433
|
|
|
2349
2434
|
// src/mvc/Controller.ts
|
|
@@ -2755,89 +2840,6 @@ var Controller = class {
|
|
|
2755
2840
|
}
|
|
2756
2841
|
};
|
|
2757
2842
|
|
|
2758
|
-
// src/mvc/ViewEngine.ts
|
|
2759
|
-
var ViewEngine = class {
|
|
2760
|
-
static engines = /* @__PURE__ */ new Map();
|
|
2761
|
-
static register(name, renderer) {
|
|
2762
|
-
this.engines.set(name, renderer);
|
|
2763
|
-
}
|
|
2764
|
-
static has(name) {
|
|
2765
|
-
return this.engines.has(name);
|
|
2766
|
-
}
|
|
2767
|
-
static render(name, template, props) {
|
|
2768
|
-
const engine = this.engines.get(name);
|
|
2769
|
-
if (!engine) {
|
|
2770
|
-
throw new Error(`View engine "${name}" has not been registered.`);
|
|
2771
|
-
}
|
|
2772
|
-
return engine(template, props);
|
|
2773
|
-
}
|
|
2774
|
-
};
|
|
2775
|
-
|
|
2776
|
-
// src/providers/InertiaServiceProvider.ts
|
|
2777
|
-
var InertiaServiceProvider = class extends ServiceProvider {
|
|
2778
|
-
register() {
|
|
2779
|
-
if (!ViewEngine.has("inertia")) {
|
|
2780
|
-
ViewEngine.register("inertia", inertia);
|
|
2781
|
-
}
|
|
2782
|
-
}
|
|
2783
|
-
boot() {
|
|
2784
|
-
this.registerValidationRenderer();
|
|
2785
|
-
this.registerSharedErrors();
|
|
2786
|
-
}
|
|
2787
|
-
/**
|
|
2788
|
-
* Register a custom renderer for ValidationException on Inertia requests.
|
|
2789
|
-
* Non-Inertia requests fall through to the default JSON 422 response.
|
|
2790
|
-
*/
|
|
2791
|
-
registerValidationRenderer() {
|
|
2792
|
-
let handler;
|
|
2793
|
-
try {
|
|
2794
|
-
handler = this.container.make("exception.handler");
|
|
2795
|
-
} catch {
|
|
2796
|
-
return;
|
|
2797
|
-
}
|
|
2798
|
-
handler.render(ValidationException, (error, ctx) => {
|
|
2799
|
-
const isInertia = ctx.req.header("X-Inertia");
|
|
2800
|
-
if (!isInertia) {
|
|
2801
|
-
return ctx.json(
|
|
2802
|
-
{
|
|
2803
|
-
message: error.message,
|
|
2804
|
-
errors: error.errors
|
|
2805
|
-
},
|
|
2806
|
-
422
|
|
2807
|
-
);
|
|
2808
|
-
}
|
|
2809
|
-
const session = getSessionFromContext(ctx);
|
|
2810
|
-
if (session) {
|
|
2811
|
-
const flattened = {};
|
|
2812
|
-
for (const [key, messages] of Object.entries(error.errors ?? {})) {
|
|
2813
|
-
flattened[key] = messages[0] ?? "";
|
|
2814
|
-
}
|
|
2815
|
-
session.flash("errors", flattened);
|
|
2816
|
-
}
|
|
2817
|
-
const referer = ctx.req.header("Referer") ?? "/";
|
|
2818
|
-
return new Response(null, {
|
|
2819
|
-
status: 303,
|
|
2820
|
-
headers: { Location: referer }
|
|
2821
|
-
});
|
|
2822
|
-
});
|
|
2823
|
-
}
|
|
2824
|
-
/**
|
|
2825
|
-
* Wrap existing shared props resolver to auto-inject flashed `errors`.
|
|
2826
|
-
*/
|
|
2827
|
-
registerSharedErrors() {
|
|
2828
|
-
const previous = getInertiaSharedPropsResolver();
|
|
2829
|
-
setInertiaSharedProps(async (ctx) => {
|
|
2830
|
-
const prev = previous ? await previous(ctx) : {};
|
|
2831
|
-
const session = getSessionFromContext(ctx);
|
|
2832
|
-
const errors = session?.getFlash("errors");
|
|
2833
|
-
return {
|
|
2834
|
-
...prev,
|
|
2835
|
-
...errors && Object.keys(errors).length > 0 ? { errors } : {}
|
|
2836
|
-
};
|
|
2837
|
-
});
|
|
2838
|
-
}
|
|
2839
|
-
};
|
|
2840
|
-
|
|
2841
2843
|
// src/providers/OAuthServiceProvider.ts
|
|
2842
2844
|
var OAuthServiceProvider = class extends ServiceProvider {
|
|
2843
2845
|
register() {
|