@alignable/bifrost-fastify 1.0.3 → 1.0.5
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/index.d.ts +4 -2
- package/dist/index.js +20 -19
- package/dist/index.js.map +1 -1
- package/package.json +13 -15
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FastifyPluginAsync } from 'fastify';
|
|
2
2
|
import { FastifyRequest } from 'fastify/types/request';
|
|
3
|
+
import { FastifyHttpProxyOptions } from '@fastify/http-proxy';
|
|
3
4
|
import { GetLayout } from '@alignable/bifrost/config';
|
|
4
5
|
import { renderPage } from 'vike/server';
|
|
5
6
|
import { PageContextServer } from 'vike/types';
|
|
@@ -13,10 +14,11 @@ declare module "fastify" {
|
|
|
13
14
|
interface FastifyRequest {
|
|
14
15
|
bifrostPageId?: string | null;
|
|
15
16
|
vikePageContext?: Partial<PageContextServer> | null;
|
|
16
|
-
getLayout: GetLayout;
|
|
17
|
+
getLayout: GetLayout | null;
|
|
18
|
+
customPageContextInit: Partial<Omit<PageContextServer, "headers">> | null;
|
|
17
19
|
}
|
|
18
20
|
}
|
|
19
|
-
interface ViteProxyPluginOptions {
|
|
21
|
+
interface ViteProxyPluginOptions extends Omit<FastifyHttpProxyOptions, "upstream" | "preHandler" | "replyOptions"> {
|
|
20
22
|
upstream: URL;
|
|
21
23
|
host: URL;
|
|
22
24
|
onError?: (error: any, pageContext: RenderedPageContext) => void;
|
package/dist/index.js
CHANGED
|
@@ -35,15 +35,10 @@ function extractDomElements(html) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
// index.ts
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
stream.on("error", (err) => reject(err));
|
|
43
|
-
stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
var viteProxyPlugin = async (fastify, { upstream, host, onError, buildPageContextInit }) => {
|
|
38
|
+
import { Http2ServerRequest } from "http2";
|
|
39
|
+
import { text } from "stream/consumers";
|
|
40
|
+
var viteProxyPlugin = async (fastify, opts) => {
|
|
41
|
+
const { upstream, host, onError, buildPageContextInit } = opts;
|
|
47
42
|
async function replyWithPage(reply, pageContext) {
|
|
48
43
|
const { httpResponse } = pageContext;
|
|
49
44
|
if (onError && httpResponse?.statusCode === 500 && pageContext.errorWhileRendering) {
|
|
@@ -61,15 +56,18 @@ var viteProxyPlugin = async (fastify, { upstream, host, onError, buildPageContex
|
|
|
61
56
|
fastify.decorateRequest("bifrostPageId", null);
|
|
62
57
|
fastify.decorateRequest("vikePageContext", null);
|
|
63
58
|
fastify.decorateRequest("getLayout", null);
|
|
59
|
+
fastify.decorateRequest("customPageContextInit", null);
|
|
64
60
|
await fastify.register(proxy, {
|
|
61
|
+
...opts,
|
|
65
62
|
upstream: upstream.href,
|
|
66
63
|
websocket: true,
|
|
67
64
|
async preHandler(req, reply) {
|
|
68
65
|
if ((req.method === "GET" || req.method === "HEAD") && req.accepts().type(["html"]) === "html") {
|
|
66
|
+
req.customPageContextInit = buildPageContextInit ? await buildPageContextInit(req) : {};
|
|
69
67
|
const pageContextInit = {
|
|
70
68
|
urlOriginal: req.url,
|
|
71
69
|
headersOriginal: req.headers,
|
|
72
|
-
...
|
|
70
|
+
...req.customPageContextInit
|
|
73
71
|
};
|
|
74
72
|
const pageContext = await renderPage(pageContextInit);
|
|
75
73
|
req.bifrostPageId = pageContext.pageId;
|
|
@@ -117,11 +115,13 @@ var viteProxyPlugin = async (fastify, { upstream, host, onError, buildPageContex
|
|
|
117
115
|
},
|
|
118
116
|
replyOptions: {
|
|
119
117
|
rewriteRequestHeaders(request, headers) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
118
|
+
if (!(request.raw instanceof Http2ServerRequest)) {
|
|
119
|
+
const fwd = forwarded(request.raw).reverse();
|
|
120
|
+
headers["X-Forwarded-For"] = fwd.join(", ");
|
|
121
|
+
headers["X-Forwarded-Host"] = host.host;
|
|
122
|
+
headers["X-Forwarded-Proto"] = host.protocol;
|
|
123
|
+
}
|
|
124
|
+
if (request.raw._bfproxy) {
|
|
125
125
|
delete headers["if-modified-since"];
|
|
126
126
|
delete headers["if-none-match"];
|
|
127
127
|
delete headers["if-unmodified-since"];
|
|
@@ -140,14 +140,14 @@ var viteProxyPlugin = async (fastify, { upstream, host, onError, buildPageContex
|
|
|
140
140
|
url.protocol = host.protocol;
|
|
141
141
|
}
|
|
142
142
|
reply.header("location", url);
|
|
143
|
-
return reply.send(res);
|
|
143
|
+
return reply.send("stream" in res ? res.stream : res);
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
const proxyLayoutInfo = req.getLayout?.(reply.getHeaders());
|
|
147
147
|
if (!proxyLayoutInfo) {
|
|
148
|
-
return reply.send(res);
|
|
148
|
+
return reply.send("stream" in res ? res.stream : res);
|
|
149
149
|
}
|
|
150
|
-
const html = await
|
|
150
|
+
const html = await text(res.stream);
|
|
151
151
|
const { bodyAttributes, bodyInnerHtml, headInnerHtml } = extractDomElements(html);
|
|
152
152
|
if (!bodyInnerHtml || !headInnerHtml) {
|
|
153
153
|
throw new Error("Proxy failed");
|
|
@@ -162,7 +162,8 @@ var viteProxyPlugin = async (fastify, { upstream, host, onError, buildPageContex
|
|
|
162
162
|
bodyInnerHtml,
|
|
163
163
|
headInnerHtml,
|
|
164
164
|
proxyLayoutInfo
|
|
165
|
-
}
|
|
165
|
+
},
|
|
166
|
+
...req.customPageContextInit
|
|
166
167
|
};
|
|
167
168
|
const pageContext = await renderPage(pageContextInit);
|
|
168
169
|
return replyWithPage(reply, pageContext);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../index.ts","../lib/extractDomElements.ts"],"sourcesContent":["// Note that this file isn't processed by Vite, see https://github.com/brillout/vike/issues/562\nimport { FastifyReply, RawServerBase, FastifyPluginAsync } from \"fastify\";\nimport { FastifyRequest, RequestGenericInterface } from \"fastify/types/request\";\nimport proxy from \"@fastify/http-proxy\";\nimport accepts from \"@fastify/accepts\";\nimport forwarded from \"@fastify/forwarded\";\nimport type { GetLayout, WrappedServerOnly } from \"@alignable/bifrost/config\";\nimport { PassThrough, Writable } from \"stream\";\nimport { IncomingMessage } from \"http\";\nimport { renderPage } from \"vike/server\";\nimport { PageContextServer } from \"vike/types\";\nimport { extractDomElements } from \"./lib/extractDomElements\";\n\ntype RenderedPageContext = Awaited<\n ReturnType<\n typeof renderPage<\n {\n isClientSideNavigation?: boolean;\n },\n { urlOriginal: string }\n >\n >\n>;\n\ndeclare module \"fastify\" {\n interface FastifyRequest {\n bifrostPageId?: string | null;\n vikePageContext?: Partial<PageContextServer> | null;\n getLayout: GetLayout;\n }\n}\n\ntype RawRequestExtendedWithProxy = FastifyRequest<\n RequestGenericInterface,\n RawServerBase\n>[\"raw\"] & {\n _bfproxy?: boolean;\n};\n\nfunction streamToString(stream: Writable): Promise<string> {\n const chunks: Buffer[] = [];\n return new Promise((resolve, reject) => {\n stream.on(\"data\", (chunk) => chunks.push(Buffer.from(chunk)));\n stream.on(\"error\", (err) => reject(err));\n stream.on(\"end\", () => resolve(Buffer.concat(chunks).toString(\"utf8\")));\n });\n}\n\ninterface ViteProxyPluginOptions {\n upstream: URL;\n host: URL;\n onError?: (error: any, pageContext: RenderedPageContext) => void;\n buildPageContextInit?: (\n req: FastifyRequest\n ) => Promise<Partial<Omit<PageContextServer, \"headers\">>>;\n}\n/**\n * Fastify plugin that wraps @fasitfy/http-proxy to proxy Rails/Turbolinks server into a vike site.\n */\nexport const viteProxyPlugin: FastifyPluginAsync<\n ViteProxyPluginOptions\n> = async (fastify, { upstream, host, onError, buildPageContextInit }) => {\n async function replyWithPage(\n reply: FastifyReply<RawServerBase>,\n pageContext: RenderedPageContext\n ): Promise<FastifyReply> {\n const { httpResponse } = pageContext;\n\n if (\n onError &&\n httpResponse?.statusCode === 500 &&\n pageContext.errorWhileRendering\n ) {\n onError(pageContext.errorWhileRendering, pageContext);\n }\n\n if (!httpResponse) {\n return reply.code(404).type(\"text/html\").send(\"Not Found\");\n }\n\n const { pipe, statusCode, headers } = httpResponse;\n const stream = new PassThrough();\n pipe(stream);\n return reply\n .status(statusCode)\n .headers(Object.fromEntries(headers))\n .send(stream);\n }\n await fastify.register(accepts);\n fastify.decorateRequest(\"bifrostPageId\", null);\n fastify.decorateRequest(\"vikePageContext\", null);\n fastify.decorateRequest(\"getLayout\", null);\n await fastify.register(proxy, {\n upstream: upstream.href,\n websocket: true,\n async preHandler(req, reply) {\n if (\n (req.method === \"GET\" || req.method === \"HEAD\") &&\n req.accepts().type([\"html\"]) === \"html\"\n ) {\n const pageContextInit = {\n urlOriginal: req.url,\n headersOriginal: req.headers,\n ...(buildPageContextInit ? await buildPageContextInit(req) : {}),\n };\n\n const pageContext = await renderPage(pageContextInit);\n\n // this does not handle getting the original pageId when errors are thrown: https://github.com/vikejs/vike/issues/1112\n req.bifrostPageId = pageContext.pageId;\n req.vikePageContext = pageContext;\n\n const proxyMode = pageContext.config?.proxyMode;\n\n switch (proxyMode) {\n case \"passthru\": {\n req.log.info(`bifrost: passthru proxy to backend`);\n return;\n }\n case \"wrapped\": {\n req.log.info(`bifrost: proxy route matched, proxying to backend`);\n if (!!pageContext.isClientSideNavigation) {\n // This should never happen because wrapped proxy routes have no onBeforeRender. onRenderClient should make a request to the legacy backend.\n req.log.error(\n \"Wrapped proxy route is requesting index.pageContext.json. Something is wrong with the client.\"\n );\n return reply.redirect(\n req.url.replace(\"/index.pageContext.json\", \"\")\n );\n }\n if (!pageContext.config?.getLayout) {\n req.log.error(\n \"Config missing getLayout on wrapped route! Falling back to passthru proxy\"\n );\n return;\n }\n\n let proxyHeadersAlreadySet = true;\n for (const [key, val] of Object.entries(\n pageContext.config?.proxyHeaders || {}\n )) {\n proxyHeadersAlreadySet &&= req.headers[key.toLowerCase()] == val;\n req.headers[key.toLowerCase()] = val;\n }\n // If proxy headers set, this is a client navigation meant to go direct to legacy backend.\n // Use passthru proxy in this case.\n // ALB CANNOT be used for this. see `onBeforeRenderClient` for details\n if (proxyHeadersAlreadySet) return;\n\n (req.raw as RawRequestExtendedWithProxy)._bfproxy = true;\n req.getLayout = pageContext.config.getLayout;\n return;\n }\n default:\n req.log.info(`bifrost: rendering page ${pageContext.pageId}`);\n return replyWithPage(reply, pageContext);\n }\n }\n },\n replyOptions: {\n rewriteRequestHeaders(request, headers) {\n const fwd = forwarded(request as IncomingMessage).reverse();\n // fwd.push(request.ip); TODO: not sure if this is needed\n headers[\"X-Forwarded-For\"] = fwd.join(\", \");\n headers[\"X-Forwarded-Host\"] = host.host;\n headers[\"X-Forwarded-Proto\"] = host.protocol;\n\n if ((request as RawRequestExtendedWithProxy)._bfproxy) {\n // Proxying and wrapping\n\n // Delete cache headers\n delete headers[\"if-modified-since\"];\n delete headers[\"if-none-match\"];\n delete headers[\"if-unmodified-since\"];\n delete headers[\"if-none-match\"];\n delete headers[\"if-range\"];\n }\n return headers;\n },\n async onResponse(req, reply: FastifyReply<RawServerBase>, res) {\n if ([301, 302, 303, 307, 308].includes(reply.statusCode)) {\n const location = reply.getHeader(\"location\") as string;\n if (location) {\n const url = new URL(location, host.href);\n if (url.host === upstream.host || url.host === host.host) {\n // rewrite redirect on upstream's host to the proxy host\n url.host = host.host;\n url.protocol = host.protocol;\n }\n reply.header(\"location\", url);\n return reply.send(res);\n }\n }\n\n const proxyLayoutInfo = req.getLayout?.(reply.getHeaders());\n if (!proxyLayoutInfo) {\n return reply.send(res);\n }\n\n const html = await streamToString(res);\n\n const { bodyAttributes, bodyInnerHtml, headInnerHtml } =\n extractDomElements(html);\n\n if (!bodyInnerHtml || !headInnerHtml) {\n throw new Error(\"Proxy failed\");\n }\n\n const pageContextInit = {\n urlOriginal: req.url,\n headersOriginal: req.headers,\n // Critical that we don't set any passToClient values in pageContextInit\n // If we do, Vike re-requests pageContext on client navigation. This breaks wrapped proxy.\n _wrappedServerOnly: {\n bodyAttributes,\n bodyInnerHtml,\n headInnerHtml,\n proxyLayoutInfo,\n } satisfies WrappedServerOnly,\n };\n const pageContext = await renderPage(pageContextInit);\n return replyWithPage(reply, pageContext);\n },\n },\n });\n};\n","import { Parser } from \"htmlparser2\";\nimport { DomHandler, type Element } from \"domhandler\";\nimport { findOne } from \"domutils\";\nimport render from \"dom-serializer\";\n\nfunction getInnerHtml(element: Element): string {\n return element.children.map((c) => render(c)).join(\"\");\n}\n\nexport function extractDomElements(html: string): {\n bodyInnerHtml: string | null;\n headInnerHtml: string | null;\n bodyAttributes: Record<string, string>;\n} {\n let bodyInnerHtml: string | null = null;\n let headInnerHtml: string | null = null;\n let bodyAttributes: Record<string, string> = {};\n const handler = new DomHandler((error, dom) => {\n if (!error) {\n const body = findOne((elem) => elem.name === \"body\", dom);\n const head = findOne((elem) => elem.name === \"head\", dom);\n if (body && head) {\n bodyAttributes = body.attribs;\n bodyInnerHtml = getInnerHtml(body);\n headInnerHtml = getInnerHtml(head);\n }\n }\n });\n const parser = new Parser(handler);\n parser.write(html);\n parser.end();\n return { bodyInnerHtml, headInnerHtml, bodyAttributes };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../index.ts","../lib/extractDomElements.ts"],"sourcesContent":["// Note that this file isn't processed by Vite, see https://github.com/brillout/vike/issues/562\nimport {\n FastifyReply,\n RawServerBase,\n FastifyPluginAsync,\n RouteGenericInterface,\n} from \"fastify\";\nimport { FastifyRequest, RequestGenericInterface } from \"fastify/types/request\";\nimport proxy, { type FastifyHttpProxyOptions } from \"@fastify/http-proxy\";\nimport accepts from \"@fastify/accepts\";\nimport forwarded from \"@fastify/forwarded\";\nimport type { GetLayout, WrappedServerOnly } from \"@alignable/bifrost/config\";\nimport { PassThrough, Writable } from \"stream\";\nimport { renderPage } from \"vike/server\";\nimport { PageContextServer } from \"vike/types\";\nimport { extractDomElements } from \"./lib/extractDomElements\";\nimport { Http2ServerRequest } from \"http2\";\nimport { IncomingMessage } from \"http\";\nimport { text } from \"node:stream/consumers\";\n\ntype RenderedPageContext = Awaited<\n ReturnType<\n typeof renderPage<\n {\n isClientSideNavigation?: boolean;\n },\n { urlOriginal: string }\n >\n >\n>;\n\ndeclare module \"fastify\" {\n interface FastifyRequest {\n bifrostPageId?: string | null;\n vikePageContext?: Partial<PageContextServer> | null;\n getLayout: GetLayout | null;\n customPageContextInit: Partial<Omit<PageContextServer, \"headers\">> | null;\n }\n}\n\ntype RawRequestExtendedWithProxy = FastifyRequest<\n RequestGenericInterface,\n RawServerBase\n>[\"raw\"] & {\n _bfproxy?: boolean;\n};\n\ninterface ViteProxyPluginOptions\n extends Omit<\n FastifyHttpProxyOptions,\n \"upstream\" | \"preHandler\" | \"replyOptions\"\n > {\n upstream: URL;\n host: URL;\n onError?: (error: any, pageContext: RenderedPageContext) => void;\n buildPageContextInit?: (\n req: FastifyRequest\n ) => Promise<Partial<Omit<PageContextServer, \"headers\">>>;\n}\n/**\n * Fastify plugin that wraps @fasitfy/http-proxy to proxy Rails/Turbolinks server into a vike site.\n */\nexport const viteProxyPlugin: FastifyPluginAsync<\n ViteProxyPluginOptions\n> = async (fastify, opts) => {\n const { upstream, host, onError, buildPageContextInit } = opts;\n async function replyWithPage(\n reply: FastifyReply<RouteGenericInterface, RawServerBase>,\n pageContext: RenderedPageContext\n ): Promise<FastifyReply> {\n const { httpResponse } = pageContext;\n\n if (\n onError &&\n httpResponse?.statusCode === 500 &&\n pageContext.errorWhileRendering\n ) {\n onError(pageContext.errorWhileRendering, pageContext);\n }\n\n if (!httpResponse) {\n return reply.code(404).type(\"text/html\").send(\"Not Found\");\n }\n\n const { pipe, statusCode, headers } = httpResponse;\n const stream = new PassThrough();\n pipe(stream);\n return reply\n .status(statusCode)\n .headers(Object.fromEntries(headers))\n .send(stream);\n }\n await fastify.register(accepts);\n fastify.decorateRequest(\"bifrostPageId\", null);\n fastify.decorateRequest(\"vikePageContext\", null);\n fastify.decorateRequest(\"getLayout\", null);\n fastify.decorateRequest(\"customPageContextInit\", null);\n await fastify.register(proxy, {\n ...opts,\n upstream: upstream.href,\n websocket: true,\n async preHandler(req, reply) {\n if (\n (req.method === \"GET\" || req.method === \"HEAD\") &&\n req.accepts().type([\"html\"]) === \"html\"\n ) {\n req.customPageContextInit = buildPageContextInit\n ? await buildPageContextInit(req)\n : {};\n\n const pageContextInit = {\n urlOriginal: req.url,\n headersOriginal: req.headers,\n ...req.customPageContextInit,\n };\n\n const pageContext = await renderPage(pageContextInit);\n\n // this does not handle getting the original pageId when errors are thrown: https://github.com/vikejs/vike/issues/1112\n req.bifrostPageId = pageContext.pageId;\n req.vikePageContext = pageContext;\n\n const proxyMode = pageContext.config?.proxyMode;\n\n switch (proxyMode) {\n case \"passthru\": {\n req.log.info(`bifrost: passthru proxy to backend`);\n return;\n }\n case \"wrapped\": {\n req.log.info(`bifrost: proxy route matched, proxying to backend`);\n if (!!pageContext.isClientSideNavigation) {\n // This should never happen because wrapped proxy routes have no onBeforeRender. onRenderClient should make a request to the legacy backend.\n req.log.error(\n \"Wrapped proxy route is requesting index.pageContext.json. Something is wrong with the client.\"\n );\n return reply.redirect(\n req.url.replace(\"/index.pageContext.json\", \"\")\n );\n }\n if (!pageContext.config?.getLayout) {\n req.log.error(\n \"Config missing getLayout on wrapped route! Falling back to passthru proxy\"\n );\n return;\n }\n\n let proxyHeadersAlreadySet = true;\n for (const [key, val] of Object.entries(\n pageContext.config?.proxyHeaders || {}\n )) {\n proxyHeadersAlreadySet &&= req.headers[key.toLowerCase()] == val;\n req.headers[key.toLowerCase()] = val;\n }\n // If proxy headers set, this is a client navigation meant to go direct to legacy backend.\n // Use passthru proxy in this case.\n // ALB CANNOT be used for this. see `onBeforeRenderClient` for details\n if (proxyHeadersAlreadySet) return;\n\n (req.raw as RawRequestExtendedWithProxy)._bfproxy = true;\n req.getLayout = pageContext.config.getLayout;\n return;\n }\n default:\n req.log.info(`bifrost: rendering page ${pageContext.pageId}`);\n return replyWithPage(reply, pageContext);\n }\n }\n },\n replyOptions: {\n rewriteRequestHeaders(request, headers) {\n if (!(request.raw instanceof Http2ServerRequest)) {\n const fwd = forwarded(request.raw).reverse();\n headers[\"X-Forwarded-For\"] = fwd.join(\", \");\n headers[\"X-Forwarded-Host\"] = host.host;\n headers[\"X-Forwarded-Proto\"] = host.protocol;\n }\n\n if ((request.raw as RawRequestExtendedWithProxy)._bfproxy) {\n // Proxying and wrapping\n\n // Delete cache headers\n delete headers[\"if-modified-since\"];\n delete headers[\"if-none-match\"];\n delete headers[\"if-unmodified-since\"];\n delete headers[\"if-none-match\"];\n delete headers[\"if-range\"];\n }\n return headers;\n },\n async onResponse(req, reply, res) {\n if ([301, 302, 303, 307, 308].includes(reply.statusCode)) {\n const location = reply.getHeader(\"location\") as string;\n if (location) {\n const url = new URL(location, host.href);\n if (url.host === upstream.host || url.host === host.host) {\n // rewrite redirect on upstream's host to the proxy host\n url.host = host.host;\n url.protocol = host.protocol;\n }\n reply.header(\"location\", url);\n return reply.send(\"stream\" in res ? res.stream : res);\n }\n }\n\n const proxyLayoutInfo = req.getLayout?.(reply.getHeaders());\n if (!proxyLayoutInfo) {\n return reply.send(\"stream\" in res ? res.stream : res);\n }\n\n const html = await text(res.stream);\n\n const { bodyAttributes, bodyInnerHtml, headInnerHtml } =\n extractDomElements(html);\n\n if (!bodyInnerHtml || !headInnerHtml) {\n throw new Error(\"Proxy failed\");\n }\n\n const pageContextInit = {\n urlOriginal: req.url,\n headersOriginal: req.headers,\n // Critical that we don't set any passToClient values in pageContextInit\n // If we do, Vike re-requests pageContext on client navigation. This breaks wrapped proxy.\n _wrappedServerOnly: {\n bodyAttributes,\n bodyInnerHtml,\n headInnerHtml,\n proxyLayoutInfo,\n } satisfies WrappedServerOnly,\n ...req.customPageContextInit,\n };\n const pageContext = await renderPage(pageContextInit);\n return replyWithPage(reply, pageContext);\n },\n },\n });\n};\n","import { Parser } from \"htmlparser2\";\nimport { DomHandler, type Element } from \"domhandler\";\nimport { findOne } from \"domutils\";\nimport render from \"dom-serializer\";\n\nfunction getInnerHtml(element: Element): string {\n return element.children.map((c) => render(c)).join(\"\");\n}\n\nexport function extractDomElements(html: string): {\n bodyInnerHtml: string | null;\n headInnerHtml: string | null;\n bodyAttributes: Record<string, string>;\n} {\n let bodyInnerHtml: string | null = null;\n let headInnerHtml: string | null = null;\n let bodyAttributes: Record<string, string> = {};\n const handler = new DomHandler((error, dom) => {\n if (!error) {\n const body = findOne((elem) => elem.name === \"body\", dom);\n const head = findOne((elem) => elem.name === \"head\", dom);\n if (body && head) {\n bodyAttributes = body.attribs;\n bodyInnerHtml = getInnerHtml(body);\n headInnerHtml = getInnerHtml(head);\n }\n }\n });\n const parser = new Parser(handler);\n parser.write(html);\n parser.end();\n return { bodyInnerHtml, headInnerHtml, bodyAttributes };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,39 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alignable/bifrost-fastify",
|
|
3
3
|
"repository": "https://github.com/Alignable/bifrost.git",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.5",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@fastify/accepts": "^
|
|
10
|
-
"@fastify/forwarded": "^
|
|
11
|
-
"@fastify/http-proxy": "^
|
|
12
|
-
"@fastify/reply-from": "^
|
|
13
|
-
"compression": "^1.7.4",
|
|
14
|
-
"cross-env": "^7.0.3",
|
|
9
|
+
"@fastify/accepts": "^5.0.4",
|
|
10
|
+
"@fastify/forwarded": "^3.0.1",
|
|
11
|
+
"@fastify/http-proxy": "^11.4.1",
|
|
12
|
+
"@fastify/reply-from": "^12.5.0",
|
|
15
13
|
"dom-serializer": "^2.0.0",
|
|
16
14
|
"domhandler": "^5.0.3",
|
|
17
15
|
"domutils": "^3.2.2",
|
|
18
|
-
"
|
|
19
|
-
"htmlparser2": "^10.0.0",
|
|
20
|
-
"sirv": "^2.0.2",
|
|
21
|
-
"vite": "^6.3.5"
|
|
16
|
+
"htmlparser2": "^10.0.0"
|
|
22
17
|
},
|
|
23
18
|
"peerDependencies": {
|
|
24
|
-
"@alignable/bifrost": "1.0.
|
|
25
|
-
"fastify": "^
|
|
19
|
+
"@alignable/bifrost": "1.0.5",
|
|
20
|
+
"fastify": "^5.0.0",
|
|
21
|
+
"vite": ">=6",
|
|
26
22
|
"vike": "0.4.247"
|
|
27
23
|
},
|
|
28
24
|
"devDependencies": {
|
|
29
25
|
"@types/connect": "^3.4.35",
|
|
30
|
-
"@types/node": "^
|
|
26
|
+
"@types/node": "^20.0.0",
|
|
31
27
|
"@types/react": "^19.0.0",
|
|
32
28
|
"@types/react-dom": "^19.0.0",
|
|
29
|
+
"fastify": "^5.0.0",
|
|
33
30
|
"react": "^19.0.0",
|
|
34
31
|
"react-dom": "^19.0.0",
|
|
35
32
|
"tsup": "^6.7.0",
|
|
36
|
-
"typescript": "^5.0.4"
|
|
33
|
+
"typescript": "^5.0.4",
|
|
34
|
+
"vite": "^6.3.5"
|
|
37
35
|
},
|
|
38
36
|
"scripts": {
|
|
39
37
|
"build": "tsup",
|