@alignable/bifrost-fastify 0.0.22 → 0.0.24

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 CHANGED
@@ -2,11 +2,20 @@ import { FastifyPluginAsync, FastifyReply, RawServerBase } from 'fastify';
2
2
  import { FastifyRequest } from 'fastify/types/request';
3
3
  import { IncomingMessage, IncomingHttpHeaders as IncomingHttpHeaders$1 } from 'http';
4
4
  import { Http2ServerRequest, IncomingHttpHeaders } from 'http2';
5
+ import { renderPage } from 'vite-plugin-ssr/server';
5
6
  import { AugmentMe } from '@alignable/bifrost';
6
7
 
8
+ type RenderedPageContext = Awaited<ReturnType<typeof renderPage<{
9
+ redirectTo?: string;
10
+ isClientSideNavigation?: boolean;
11
+ _pageId?: string;
12
+ }, {
13
+ urlOriginal: string;
14
+ }>>>;
7
15
  interface ViteProxyPluginOptions {
8
16
  upstream: URL;
9
17
  host: URL;
18
+ onError?: (error: any, pageContext: RenderedPageContext) => void;
10
19
  buildPageContextInit?: (req: FastifyRequest) => Promise<AugmentMe.PageContextInit>;
11
20
  getLayout: (reply: FastifyReply<RawServerBase>) => {
12
21
  layout: string;
package/dist/index.js CHANGED
@@ -11,30 +11,34 @@ function streamToString(stream) {
11
11
  stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
12
12
  });
13
13
  }
14
- async function replyWithPage(reply, pageContext) {
15
- const { httpResponse } = pageContext;
16
- if (pageContext.redirectTo && !pageContext.isClientSideNavigation) {
17
- return reply.redirect(307, pageContext.redirectTo);
18
- }
19
- if (!httpResponse) {
20
- return reply.code(404).type("text/html").send("Not Found");
21
- }
22
- const { body, statusCode, contentType } = httpResponse;
23
- return reply.status(statusCode).type(contentType).send(body);
24
- }
25
14
  var proxyPageId = "/proxy/pages";
26
- var viteProxyPlugin = async (fastify, { upstream, host, buildPageContextInit, rewriteRequestHeaders, getLayout }) => {
15
+ var viteProxyPlugin = async (fastify, {
16
+ upstream,
17
+ host,
18
+ onError,
19
+ buildPageContextInit,
20
+ rewriteRequestHeaders,
21
+ getLayout
22
+ }) => {
23
+ async function replyWithPage(reply, pageContext) {
24
+ const { httpResponse } = pageContext;
25
+ if (onError && httpResponse?.statusCode === 500 && pageContext.errorWhileRendering) {
26
+ onError(pageContext.errorWhileRendering, pageContext);
27
+ }
28
+ if (pageContext.redirectTo && !pageContext.isClientSideNavigation) {
29
+ return reply.redirect(307, pageContext.redirectTo);
30
+ }
31
+ if (!httpResponse) {
32
+ return reply.code(404).type("text/html").send("Not Found");
33
+ }
34
+ const { body, statusCode, headers } = httpResponse;
35
+ return reply.status(statusCode).headers(Object.fromEntries(headers)).send(body);
36
+ }
27
37
  await fastify.register(accepts);
28
38
  await fastify.register(proxy, {
29
39
  upstream: upstream.href,
30
40
  async preHandler(req, reply) {
31
41
  if (req.method === "GET" && req.accepts().type(["html"]) === "html") {
32
- const trailingSlash = /\/(\?|#|$)/;
33
- if (trailingSlash.test(req.url)) {
34
- req.log.info("bifrost: redirecting trailing slash");
35
- reply.redirect(301, req.url.replace(trailingSlash, "$1"));
36
- return;
37
- }
38
42
  const pageContextInit = {
39
43
  urlOriginal: req.url,
40
44
  ...buildPageContextInit ? await buildPageContextInit(req) : {}
@@ -50,12 +54,18 @@ var viteProxyPlugin = async (fastify, { upstream, host, buildPageContextInit, re
50
54
  return replyWithPage(reply, pageContext);
51
55
  } else {
52
56
  req.log.info(`bifrost: proxy route matched, proxying to backend`);
57
+ const isPageContext = req.raw.url.includes(
58
+ "/index.pageContext.json"
59
+ );
53
60
  req._proxy = {
54
- isPageContext: req.raw.url.includes("/index.pageContext.json"),
61
+ isPageContext,
55
62
  originalUrl: req.raw.url
56
63
  };
57
64
  req.raw._proxy = true;
58
- req.raw.url = req.raw.url.replace("/index.pageContext.json", "");
65
+ if (isPageContext) {
66
+ req.raw.headers["accept"] = "text/html";
67
+ req.raw.url = req.raw.url.replace("/index.pageContext.json", "");
68
+ }
59
69
  }
60
70
  }
61
71
  },
@@ -91,12 +101,10 @@ var viteProxyPlugin = async (fastify, { upstream, host, buildPageContextInit, re
91
101
  }
92
102
  reply.header("location", url);
93
103
  if (isPageContext) {
94
- reply.status(200).type("application/json").send(
104
+ return reply.status(200).type("application/json").send(
95
105
  JSON.stringify({
96
- pageContext: {
97
- _pageId: proxyPageId,
98
- redirectTo: url
99
- }
106
+ _pageId: proxyPageId,
107
+ redirectTo: url
100
108
  })
101
109
  );
102
110
  } else {
@@ -105,20 +113,18 @@ var viteProxyPlugin = async (fastify, { upstream, host, buildPageContextInit, re
105
113
  }
106
114
  }
107
115
  const { layout, layoutProps } = getLayout(reply);
108
- if (!layout) {
116
+ if (!isPageContext && !layout) {
109
117
  return reply.send(res);
110
118
  }
111
- const proxy2 = await streamToString(res);
119
+ const html = await streamToString(res);
112
120
  const pageContextInit = {
113
121
  urlOriginal: req.url,
114
- layout,
115
- layoutProps
122
+ fromProxy: {
123
+ layout,
124
+ layoutProps,
125
+ html
126
+ }
116
127
  };
117
- if (isPageContext) {
118
- Object.assign(pageContextInit, { proxySendClient: proxy2 });
119
- } else {
120
- Object.assign(pageContextInit, { proxy: proxy2 });
121
- }
122
128
  const pageContext = await renderPage(pageContextInit);
123
129
  return replyWithPage(reply, pageContext);
124
130
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../index.ts"],"sourcesContent":["// Note that this file isn't processed by Vite, see https://github.com/brillout/vite-plugin-ssr/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 { type PageContextProxy } from \"../bifrost/types/internal.js\";\nimport forwarded from \"@fastify/forwarded\";\nimport { Writable } from \"stream\";\nimport { IncomingHttpHeaders, IncomingMessage } from \"http\";\nimport {\n Http2ServerRequest,\n IncomingHttpHeaders as Http2IncomingHttpHeaders,\n} from \"http2\";\nimport { renderPage } from \"vite-plugin-ssr/server\";\nimport { AugmentMe } from \"@alignable/bifrost\";\n\ntype RequestExtendedWithProxy = FastifyRequest<\n RequestGenericInterface,\n RawServerBase\n> & { _proxy?: { isPageContext: boolean; originalUrl?: string } };\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\nasync function replyWithPage(\n reply: FastifyReply<RawServerBase>,\n pageContext: Awaited<\n ReturnType<\n typeof renderPage<\n { redirectTo?: string; isClientSideNavigation?: boolean },\n {}\n >\n >\n >\n): Promise<FastifyReply> {\n const { httpResponse } = pageContext;\n\n if (pageContext.redirectTo && !pageContext.isClientSideNavigation) {\n return reply.redirect(307, pageContext.redirectTo);\n }\n\n if (!httpResponse) {\n return reply.code(404).type(\"text/html\").send(\"Not Found\");\n }\n\n const { body, statusCode, contentType } = httpResponse;\n return reply.status(statusCode).type(contentType).send(body);\n}\n\nconst proxyPageId = \"/proxy/pages\";\n\ninterface ViteProxyPluginOptions {\n upstream: URL;\n host: URL;\n buildPageContextInit?: (\n req: FastifyRequest\n ) => Promise<AugmentMe.PageContextInit>;\n getLayout: (reply: FastifyReply<RawServerBase>) => {\n layout: string;\n layoutProps: AugmentMe.LayoutProps;\n };\n /// Use to signal to legacy backend to return special results (eg. remove navbar etc)\n rewriteRequestHeaders?: (\n req: Http2ServerRequest | IncomingMessage,\n headers: Http2IncomingHttpHeaders | IncomingHttpHeaders\n ) => Http2IncomingHttpHeaders | IncomingHttpHeaders;\n}\n/**\n * Fastify plugin that wraps @fasitfy/http-proxy to proxy Rails/Turbolinks server into a Vite-Plugin-SSR site.\n */\nexport const viteProxyPlugin: FastifyPluginAsync<\n ViteProxyPluginOptions\n> = async (\n fastify,\n { upstream, host, buildPageContextInit, rewriteRequestHeaders, getLayout }\n) => {\n await fastify.register(accepts);\n await fastify.register(proxy, {\n upstream: upstream.href,\n async preHandler(req, reply) {\n if (req.method === \"GET\" && req.accepts().type([\"html\"]) === \"html\") {\n const trailingSlash = /\\/(\\?|#|$)/;\n\n if (trailingSlash.test(req.url)) {\n req.log.info(\"bifrost: redirecting trailing slash\");\n reply.redirect(301, req.url.replace(trailingSlash, \"\\$1\"));\n return;\n }\n\n const pageContextInit = {\n urlOriginal: req.url,\n ...(buildPageContextInit ? await buildPageContextInit(req) : {}),\n };\n\n const pageContext = await renderPage<\n { _pageId: string },\n typeof pageContextInit\n >(pageContextInit);\n\n const proxy = pageContext._pageId === proxyPageId;\n const noRouteMatched =\n (pageContext as any).is404 && !pageContext.errorWhileRendering; // we hit no page, but NOT because of an error\n\n if (noRouteMatched) {\n req.log.info(\"bifrost: no route match, naked proxy to backend\");\n // Naked proxy\n return;\n } else if (!proxy) {\n req.log.info(`bifrost: rendering page ${pageContext._pageId}`);\n return replyWithPage(reply, pageContext);\n } else {\n req.log.info(`bifrost: proxy route matched, proxying to backend`);\n // pageContext.json is added on client navigations to indicate we are returning just json for the client router\n // we have to remove it before proxying though.\n (req as RequestExtendedWithProxy)._proxy = {\n isPageContext: req.raw.url!.includes(\"/index.pageContext.json\"),\n originalUrl: req.raw.url,\n };\n (req.raw as any)._proxy = true;\n req.raw.url = req.raw.url!.replace(\"/index.pageContext.json\", \"\");\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-Protocol\"] = host.protocol;\n\n if ((request as any)._proxy) {\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 if (rewriteRequestHeaders) {\n return rewriteRequestHeaders(request, headers);\n }\n }\n return headers;\n },\n async onResponse(req, reply: FastifyReply<RawServerBase>, res) {\n const { isPageContext = false, originalUrl = undefined } =\n (req as RequestExtendedWithProxy)._proxy || {};\n if (isPageContext && originalUrl) {\n // restore url rewrite\n req.raw.url = originalUrl;\n }\n\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);\n if (url.host === upstream.host) {\n // rewrite redirect on upstream's host to the proxy host\n url.host = host.host;\n }\n reply.header(\"location\", url);\n if (isPageContext) {\n reply\n .status(200)\n .type(\"application/json\")\n .send(\n JSON.stringify({\n pageContext: {\n _pageId: proxyPageId,\n redirectTo: url,\n },\n })\n );\n } else {\n return reply.send(res);\n }\n }\n }\n\n const { layout, layoutProps } = getLayout(reply);\n if (!layout) {\n return reply.send(res);\n }\n\n const proxy = await streamToString(res);\n\n const pageContextInit: Partial<PageContextProxy> = {\n urlOriginal: req.url,\n layout,\n layoutProps,\n };\n if (isPageContext) {\n // proxySendClient is serialized and sent to client on subsequent navigation.\n Object.assign(pageContextInit, { proxySendClient: proxy });\n } else {\n // proxy is ONLY included server-side to avoid doubling page size\n Object.assign(pageContextInit, { proxy });\n }\n const pageContext = await renderPage(pageContextInit);\n return replyWithPage(reply, pageContext);\n },\n },\n });\n};\n"],"mappings":"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","names":["proxy"]}
1
+ {"version":3,"sources":["../index.ts"],"sourcesContent":["// Note that this file isn't processed by Vite, see https://github.com/brillout/vite-plugin-ssr/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 { Writable } from \"stream\";\nimport { IncomingHttpHeaders, IncomingMessage } from \"http\";\nimport {\n Http2ServerRequest,\n IncomingHttpHeaders as Http2IncomingHttpHeaders,\n} from \"http2\";\nimport { renderPage } from \"vite-plugin-ssr/server\";\nimport { AugmentMe } from \"@alignable/bifrost\";\n\ntype RenderedPageContext = Awaited<\n ReturnType<\n typeof renderPage<\n {\n redirectTo?: string;\n isClientSideNavigation?: boolean;\n _pageId?: string;\n },\n { urlOriginal: string }\n >\n >\n>;\n\ntype RequestExtendedWithProxy = FastifyRequest<\n RequestGenericInterface,\n RawServerBase\n> & { _proxy?: { isPageContext: boolean; originalUrl?: string } };\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\nconst proxyPageId = \"/proxy/pages\";\n\ninterface ViteProxyPluginOptions {\n upstream: URL;\n host: URL;\n onError?: (error: any, pageContext: RenderedPageContext) => void;\n buildPageContextInit?: (\n req: FastifyRequest\n ) => Promise<AugmentMe.PageContextInit>;\n getLayout: (reply: FastifyReply<RawServerBase>) => {\n layout: string;\n layoutProps: AugmentMe.LayoutProps;\n };\n /// Use to signal to legacy backend to return special results (eg. remove navbar etc)\n rewriteRequestHeaders?: (\n req: Http2ServerRequest | IncomingMessage,\n headers: Http2IncomingHttpHeaders | IncomingHttpHeaders\n ) => Http2IncomingHttpHeaders | IncomingHttpHeaders;\n}\n/**\n * Fastify plugin that wraps @fasitfy/http-proxy to proxy Rails/Turbolinks server into a Vite-Plugin-SSR site.\n */\nexport const viteProxyPlugin: FastifyPluginAsync<\n ViteProxyPluginOptions\n> = async (\n fastify,\n {\n upstream,\n host,\n onError,\n buildPageContextInit,\n rewriteRequestHeaders,\n getLayout,\n }\n) => {\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 (pageContext.redirectTo && !pageContext.isClientSideNavigation) {\n return reply.redirect(307, pageContext.redirectTo);\n }\n\n if (!httpResponse) {\n return reply.code(404).type(\"text/html\").send(\"Not Found\");\n }\n\n const { body, statusCode, headers } = httpResponse;\n return reply\n .status(statusCode)\n .headers(Object.fromEntries(headers))\n .send(body);\n }\n await fastify.register(accepts);\n await fastify.register(proxy, {\n upstream: upstream.href,\n async preHandler(req, reply) {\n if (req.method === \"GET\" && req.accepts().type([\"html\"]) === \"html\") {\n const pageContextInit = {\n urlOriginal: req.url,\n ...(buildPageContextInit ? await buildPageContextInit(req) : {}),\n };\n\n const pageContext = await renderPage<\n { _pageId: string },\n typeof pageContextInit\n >(pageContextInit);\n\n const proxy = pageContext._pageId === proxyPageId;\n const noRouteMatched =\n (pageContext as any).is404 && !pageContext.errorWhileRendering; // we hit no page, but NOT because of an error\n\n if (noRouteMatched) {\n req.log.info(\"bifrost: no route match, naked proxy to backend\");\n // Naked proxy\n return;\n } else if (!proxy) {\n req.log.info(`bifrost: rendering page ${pageContext._pageId}`);\n return replyWithPage(reply, pageContext);\n } else {\n req.log.info(`bifrost: proxy route matched, proxying to backend`);\n // pageContext.json is added on client navigations to indicate we are returning just json for the client router\n // we have to remove it before proxying though.\n const isPageContext = req.raw.url!.includes(\n \"/index.pageContext.json\"\n );\n (req as RequestExtendedWithProxy)._proxy = {\n isPageContext,\n originalUrl: req.raw.url,\n };\n (req.raw as any)._proxy = true;\n if (isPageContext) {\n // page context expects json - we need html from legacy server\n req.raw.headers[\"accept\"] = \"text/html\";\n req.raw.url = req.raw.url!.replace(\"/index.pageContext.json\", \"\");\n }\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-Protocol\"] = host.protocol;\n\n if ((request as any)._proxy) {\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 if (rewriteRequestHeaders) {\n return rewriteRequestHeaders(request, headers);\n }\n }\n return headers;\n },\n async onResponse(req, reply: FastifyReply<RawServerBase>, res) {\n const { isPageContext = false, originalUrl = undefined } =\n (req as RequestExtendedWithProxy)._proxy || {};\n if (isPageContext && originalUrl) {\n // restore url rewrite\n req.raw.url = originalUrl;\n }\n\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);\n if (url.host === upstream.host) {\n // rewrite redirect on upstream's host to the proxy host\n url.host = host.host;\n }\n reply.header(\"location\", url);\n if (isPageContext) {\n return reply\n .status(200)\n .type(\"application/json\")\n .send(\n JSON.stringify({\n _pageId: proxyPageId,\n redirectTo: url,\n })\n );\n } else {\n return reply.send(res);\n }\n }\n }\n\n const { layout, layoutProps } = getLayout(reply);\n if (!isPageContext && !layout) {\n return reply.send(res);\n }\n\n const html = await streamToString(res);\n\n const pageContextInit = {\n urlOriginal: req.url,\n fromProxy: {\n layout,\n layoutProps,\n html,\n },\n };\n const pageContext = await renderPage(pageContextInit);\n return replyWithPage(reply, pageContext);\n },\n },\n });\n};\n"],"mappings":";AAGA,OAAO,WAAW;AAClB,OAAO,aAAa;AACpB,OAAO,eAAe;AAOtB,SAAS,kBAAkB;AAqB3B,SAAS,eAAe,QAAmC;AACzD,QAAM,SAAmB,CAAC;AAC1B,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,WAAO,GAAG,QAAQ,CAAC,UAAU,OAAO,KAAK,OAAO,KAAK,KAAK,CAAC,CAAC;AAC5D,WAAO,GAAG,SAAS,CAAC,QAAQ,OAAO,GAAG,CAAC;AACvC,WAAO,GAAG,OAAO,MAAM,QAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM,CAAC,CAAC;AAAA,EACxE,CAAC;AACH;AAEA,IAAM,cAAc;AAsBb,IAAM,kBAET,OACF,SACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MACG;AACH,iBAAe,cACb,OACA,aACuB;AACvB,UAAM,EAAE,aAAa,IAAI;AAEzB,QACE,WACA,cAAc,eAAe,OAC7B,YAAY,qBACZ;AACA,cAAQ,YAAY,qBAAqB,WAAW;AAAA,IACtD;AAEA,QAAI,YAAY,cAAc,CAAC,YAAY,wBAAwB;AACjE,aAAO,MAAM,SAAS,KAAK,YAAY,UAAU;AAAA,IACnD;AAEA,QAAI,CAAC,cAAc;AACjB,aAAO,MAAM,KAAK,GAAG,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW;AAAA,IAC3D;AAEA,UAAM,EAAE,MAAM,YAAY,QAAQ,IAAI;AACtC,WAAO,MACJ,OAAO,UAAU,EACjB,QAAQ,OAAO,YAAY,OAAO,CAAC,EACnC,KAAK,IAAI;AAAA,EACd;AACA,QAAM,QAAQ,SAAS,OAAO;AAC9B,QAAM,QAAQ,SAAS,OAAO;AAAA,IAC5B,UAAU,SAAS;AAAA,IACnB,MAAM,WAAW,KAAK,OAAO;AAC3B,UAAI,IAAI,WAAW,SAAS,IAAI,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,QAAQ;AACnE,cAAM,kBAAkB;AAAA,UACtB,aAAa,IAAI;AAAA,UACjB,GAAI,uBAAuB,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAAA,QAChE;AAEA,cAAM,cAAc,MAAM,WAGxB,eAAe;AAEjB,cAAMA,SAAQ,YAAY,YAAY;AACtC,cAAM,iBACH,YAAoB,SAAS,CAAC,YAAY;AAE7C,YAAI,gBAAgB;AAClB,cAAI,IAAI,KAAK,iDAAiD;AAE9D;AAAA,QACF,WAAW,CAACA,QAAO;AACjB,cAAI,IAAI,KAAK,2BAA2B,YAAY,SAAS;AAC7D,iBAAO,cAAc,OAAO,WAAW;AAAA,QACzC,OAAO;AACL,cAAI,IAAI,KAAK,mDAAmD;AAGhE,gBAAM,gBAAgB,IAAI,IAAI,IAAK;AAAA,YACjC;AAAA,UACF;AACA,UAAC,IAAiC,SAAS;AAAA,YACzC;AAAA,YACA,aAAa,IAAI,IAAI;AAAA,UACvB;AACA,UAAC,IAAI,IAAY,SAAS;AAC1B,cAAI,eAAe;AAEjB,gBAAI,IAAI,QAAQ,QAAQ,IAAI;AAC5B,gBAAI,IAAI,MAAM,IAAI,IAAI,IAAK,QAAQ,2BAA2B,EAAE;AAAA,UAClE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,cAAc;AAAA,MACZ,sBAAsB,SAAS,SAAS;AACtC,cAAM,MAAM,UAAU,OAA0B,EAAE,QAAQ;AAE1D,gBAAQ,iBAAiB,IAAI,IAAI,KAAK,IAAI;AAC1C,gBAAQ,kBAAkB,IAAI,KAAK;AACnC,gBAAQ,sBAAsB,IAAI,KAAK;AAEvC,YAAK,QAAgB,QAAQ;AAI3B,iBAAO,QAAQ,mBAAmB;AAClC,iBAAO,QAAQ,eAAe;AAC9B,iBAAO,QAAQ,qBAAqB;AACpC,iBAAO,QAAQ,eAAe;AAC9B,iBAAO,QAAQ,UAAU;AACzB,cAAI,uBAAuB;AACzB,mBAAO,sBAAsB,SAAS,OAAO;AAAA,UAC/C;AAAA,QACF;AACA,eAAO;AAAA,MACT;AAAA,MACA,MAAM,WAAW,KAAK,OAAoC,KAAK;AAC7D,cAAM,EAAE,gBAAgB,OAAO,cAAc,OAAU,IACpD,IAAiC,UAAU,CAAC;AAC/C,YAAI,iBAAiB,aAAa;AAEhC,cAAI,IAAI,MAAM;AAAA,QAChB;AAEA,YAAI,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG,EAAE,SAAS,MAAM,UAAU,GAAG;AACxD,gBAAM,WAAW,MAAM,UAAU,UAAU;AAC3C,cAAI,UAAU;AACZ,kBAAM,MAAM,IAAI,IAAI,QAAQ;AAC5B,gBAAI,IAAI,SAAS,SAAS,MAAM;AAE9B,kBAAI,OAAO,KAAK;AAAA,YAClB;AACA,kBAAM,OAAO,YAAY,GAAG;AAC5B,gBAAI,eAAe;AACjB,qBAAO,MACJ,OAAO,GAAG,EACV,KAAK,kBAAkB,EACvB;AAAA,gBACC,KAAK,UAAU;AAAA,kBACb,SAAS;AAAA,kBACT,YAAY;AAAA,gBACd,CAAC;AAAA,cACH;AAAA,YACJ,OAAO;AACL,qBAAO,MAAM,KAAK,GAAG;AAAA,YACvB;AAAA,UACF;AAAA,QACF;AAEA,cAAM,EAAE,QAAQ,YAAY,IAAI,UAAU,KAAK;AAC/C,YAAI,CAAC,iBAAiB,CAAC,QAAQ;AAC7B,iBAAO,MAAM,KAAK,GAAG;AAAA,QACvB;AAEA,cAAM,OAAO,MAAM,eAAe,GAAG;AAErC,cAAM,kBAAkB;AAAA,UACtB,aAAa,IAAI;AAAA,UACjB,WAAW;AAAA,YACT;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF;AACA,cAAM,cAAc,MAAM,WAAW,eAAe;AACpD,eAAO,cAAc,OAAO,WAAW;AAAA,MACzC;AAAA,IACF;AAAA,EACF,CAAC;AACH;","names":["proxy"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@alignable/bifrost-fastify",
3
3
  "repository": "https://github.com/Alignable/bifrost.git",
4
- "version": "0.0.22",
4
+ "version": "0.0.24",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -18,9 +18,9 @@
18
18
  "vite": "^4.0.3"
19
19
  },
20
20
  "peerDependencies": {
21
- "@alignable/bifrost": "0.0.22",
21
+ "@alignable/bifrost": "0.0.24",
22
22
  "fastify": "^4.9.2",
23
- "vite-plugin-ssr": "0.4.131-commit-35ca471f7"
23
+ "vite-plugin-ssr": "0.4.137"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/connect": "^3.4.35",