@alignable/bifrost-fastify 0.0.39 → 0.1.0-rc.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/index.d.ts CHANGED
@@ -1,9 +1,7 @@
1
- import { FastifyPluginAsync, FastifyReply, RawServerBase } from 'fastify';
1
+ import { FastifyPluginAsync } from 'fastify';
2
2
  import { FastifyRequest } from 'fastify/types/request';
3
- import { IncomingMessage, IncomingHttpHeaders as IncomingHttpHeaders$1 } from 'http';
4
- import { Http2ServerRequest, IncomingHttpHeaders } from 'http2';
5
3
  import { renderPage } from 'vike/server';
6
- import { AugmentMe } from '@alignable/bifrost';
4
+ import { GetLayout, AugmentMe } from '@alignable/bifrost';
7
5
 
8
6
  type RenderedPageContext = Awaited<ReturnType<typeof renderPage<{
9
7
  redirectTo?: string;
@@ -15,6 +13,7 @@ type RenderedPageContext = Awaited<ReturnType<typeof renderPage<{
15
13
  declare module "fastify" {
16
14
  interface FastifyRequest {
17
15
  bifrostPageId?: string;
16
+ getLayout: GetLayout;
18
17
  }
19
18
  }
20
19
  interface ViteProxyPluginOptions {
@@ -22,11 +21,6 @@ interface ViteProxyPluginOptions {
22
21
  host: URL;
23
22
  onError?: (error: any, pageContext: RenderedPageContext) => void;
24
23
  buildPageContextInit?: (req: FastifyRequest) => Promise<AugmentMe.PageContextInit>;
25
- getLayout: (reply: FastifyReply<RawServerBase>) => {
26
- layout: string;
27
- layoutProps: AugmentMe.LayoutProps;
28
- };
29
- rewriteRequestHeaders?: (req: Http2ServerRequest | IncomingMessage, headers: IncomingHttpHeaders | IncomingHttpHeaders$1) => IncomingHttpHeaders | IncomingHttpHeaders$1;
30
24
  }
31
25
  /**
32
26
  * Fastify plugin that wraps @fasitfy/http-proxy to proxy Rails/Turbolinks server into a vike site.
package/dist/index.js CHANGED
@@ -11,16 +11,7 @@ function streamToString(stream) {
11
11
  stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
12
12
  });
13
13
  }
14
- var wrappedProxyPageId = "/proxy/pages/wrapped";
15
- var passthruProxyPageId = "/proxy/pages/passthru";
16
- var viteProxyPlugin = async (fastify, {
17
- upstream,
18
- host,
19
- onError,
20
- buildPageContextInit,
21
- rewriteRequestHeaders,
22
- getLayout
23
- }) => {
14
+ var viteProxyPlugin = async (fastify, { upstream, host, onError, buildPageContextInit }) => {
24
15
  async function replyWithPage(reply, pageContext) {
25
16
  const { httpResponse } = pageContext;
26
17
  if (onError && httpResponse?.statusCode === 500 && pageContext.errorWhileRendering) {
@@ -37,6 +28,7 @@ var viteProxyPlugin = async (fastify, {
37
28
  }
38
29
  await fastify.register(accepts);
39
30
  fastify.decorateRequest("bifrostPageId", null);
31
+ fastify.decorateRequest("getLayout", null);
40
32
  await fastify.register(proxy, {
41
33
  upstream: upstream.href,
42
34
  websocket: true,
@@ -49,25 +41,44 @@ var viteProxyPlugin = async (fastify, {
49
41
  const pageContext = await renderPage(pageContextInit);
50
42
  const originalPageId = pageContext?._debugRouteMatches?.[0]?.pageId || pageContext._pageId;
51
43
  req.bifrostPageId = originalPageId;
52
- const proxy2 = pageContext._pageId === wrappedProxyPageId;
53
- const passthruProxy = pageContext._pageId === passthruProxyPageId;
54
- if (passthruProxy) {
55
- req.log.info(`bifrost: passthru proxy to backend`);
56
- return;
57
- } else if (!proxy2) {
58
- req.log.info(`bifrost: rendering page ${pageContext._pageId}`);
59
- return replyWithPage(reply, pageContext);
60
- } else if (proxy2) {
61
- req.log.info(`bifrost: proxy route matched, proxying to backend`);
62
- const isPageContext = !!pageContext.isClientSideNavigation;
63
- req.raw._bfproxy = {
64
- isPageContext,
65
- originalUrl: req.raw.url
66
- };
67
- if (isPageContext) {
68
- req.raw.headers["accept"] = "text/html";
69
- req.raw.url = req.raw.url.replace("/index.pageContext.json", "");
44
+ const proxyMode = pageContext.config?.proxyMode;
45
+ switch (proxyMode) {
46
+ case "passthru": {
47
+ req.log.info(`bifrost: passthru proxy to backend`);
48
+ return;
70
49
  }
50
+ case "wrapped": {
51
+ req.log.info(`bifrost: proxy route matched, proxying to backend`);
52
+ if (!!pageContext.isClientSideNavigation) {
53
+ req.log.error(
54
+ "Wrapped proxy route is requesting index.pageContext.json. Something is wrong with the client."
55
+ );
56
+ return reply.redirect(
57
+ req.url.replace("/index.pageContext.json", "")
58
+ );
59
+ }
60
+ if (!pageContext.config?.getLayout) {
61
+ req.log.error(
62
+ "Config missing getLayout on wrapped route! Falling back to passthru proxy"
63
+ );
64
+ return;
65
+ }
66
+ let proxyHeadersAlreadySet = true;
67
+ for (const [key, val] of Object.entries(
68
+ pageContext.config?.proxyHeaders || {}
69
+ )) {
70
+ proxyHeadersAlreadySet && (proxyHeadersAlreadySet = req.headers[key.toLowerCase()] == val);
71
+ req.headers[key.toLowerCase()] = val;
72
+ }
73
+ if (proxyHeadersAlreadySet)
74
+ return;
75
+ req.raw._bfproxy = true;
76
+ req.getLayout = pageContext.config.getLayout;
77
+ return;
78
+ }
79
+ default:
80
+ req.log.info(`bifrost: rendering page ${pageContext._pageId}`);
81
+ return replyWithPage(reply, pageContext);
71
82
  }
72
83
  }
73
84
  },
@@ -83,17 +94,10 @@ var viteProxyPlugin = async (fastify, {
83
94
  delete headers["if-unmodified-since"];
84
95
  delete headers["if-none-match"];
85
96
  delete headers["if-range"];
86
- if (rewriteRequestHeaders) {
87
- return rewriteRequestHeaders(request, headers);
88
- }
89
97
  }
90
98
  return headers;
91
99
  },
92
100
  async onResponse(req, reply, res) {
93
- const { isPageContext = false, originalUrl = void 0 } = req.raw._bfproxy || {};
94
- if (isPageContext && originalUrl) {
95
- req.raw.url = originalUrl;
96
- }
97
101
  if ([301, 302, 303, 307, 308].includes(reply.statusCode)) {
98
102
  const location = reply.getHeader("location");
99
103
  if (location) {
@@ -103,28 +107,21 @@ var viteProxyPlugin = async (fastify, {
103
107
  url.protocol = host.protocol;
104
108
  }
105
109
  reply.header("location", url);
106
- if (isPageContext) {
107
- return reply.status(200).type("application/json").send(
108
- JSON.stringify({
109
- _pageId: wrappedProxyPageId,
110
- redirectTo: url
111
- })
112
- );
113
- } else {
114
- return reply.send(res);
115
- }
110
+ return reply.send(res);
116
111
  }
117
112
  }
118
- const { layout, layoutProps } = getLayout(reply);
119
- if (!isPageContext && !layout) {
113
+ const layoutInfo = req.getLayout?.(reply.getHeaders());
114
+ if (!layoutInfo?.layout) {
120
115
  return reply.send(res);
121
116
  }
122
117
  const html = await streamToString(res);
123
118
  const pageContextInit = {
124
119
  urlOriginal: req.url,
125
- fromProxy: {
126
- layout,
127
- layoutProps,
120
+ // Critical that we don't set any passToClient values in pageContextInit
121
+ // If we do, Vike re-requests pageContext on client navigation. This breaks wrapped proxy.
122
+ wrappedServerOnly: {
123
+ layout: layoutInfo.layout,
124
+ layoutProps: layoutInfo.layoutProps,
128
125
  html
129
126
  }
130
127
  };
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/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 { Writable } from \"stream\";\nimport { IncomingHttpHeaders, IncomingMessage } from \"http\";\nimport {\n Http2ServerRequest,\n IncomingHttpHeaders as Http2IncomingHttpHeaders,\n} from \"http2\";\nimport { renderPage } from \"vike/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\ndeclare module \"fastify\" {\n interface FastifyRequest {\n bifrostPageId?: string;\n }\n}\n\ntype RawRequestExtendedWithProxy = FastifyRequest<\n RequestGenericInterface,\n RawServerBase\n>[\"raw\"] & {\n _bfproxy?: {\n isPageContext: boolean;\n originalUrl?: string;\n };\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\nconst wrappedProxyPageId = \"/proxy/pages/wrapped\";\nconst passthruProxyPageId = \"/proxy/pages/passthru\";\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 vike 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 fastify.decorateRequest(\"bifrostPageId\", 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 ...(buildPageContextInit ? await buildPageContextInit(req) : {}),\n };\n\n const pageContext = await renderPage<\n { _pageId: string },\n typeof pageContextInit\n >(pageContextInit);\n\n // should stop relying on unstable _debugRouteMatches after this issue is closed: https://github.com/vikejs/vike/issues/1112\n const originalPageId =\n (pageContext as any)?._debugRouteMatches?.[0]?.pageId ||\n pageContext._pageId;\n req.bifrostPageId = originalPageId;\n\n const proxy = pageContext._pageId === wrappedProxyPageId;\n const passthruProxy = pageContext._pageId === passthruProxyPageId;\n\n if (passthruProxy) {\n req.log.info(`bifrost: passthru proxy to backend`);\n // passthru proxy\n return;\n } else if (!proxy) {\n req.log.info(`bifrost: rendering page ${pageContext._pageId}`);\n return replyWithPage(reply, pageContext);\n } else if (proxy) {\n req.log.info(`bifrost: proxy route matched, proxying to backend`);\n const isPageContext = !!pageContext.isClientSideNavigation;\n (req.raw as RawRequestExtendedWithProxy)._bfproxy = {\n isPageContext,\n originalUrl: req.raw.url,\n };\n if (isPageContext) {\n // page context expects json - we need html from legacy server\n req.raw.headers[\"accept\"] = \"text/html\";\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.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-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 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.raw as RawRequestExtendedWithProxy)._bfproxy || {};\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 || 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 if (isPageContext) {\n return reply\n .status(200)\n .type(\"application/json\")\n .send(\n JSON.stringify({\n _pageId: wrappedProxyPageId,\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":"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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/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 { Writable } from \"stream\";\nimport { IncomingMessage } from \"http\";\nimport { renderPage } from \"vike/server\";\nimport { AugmentMe, GetLayout, PageContext } 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\ndeclare module \"fastify\" {\n interface FastifyRequest {\n bifrostPageId?: string;\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<AugmentMe.PageContextInit>;\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 (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 fastify.decorateRequest(\"bifrostPageId\", 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 ...(buildPageContextInit ? await buildPageContextInit(req) : {}),\n };\n\n const pageContext = await renderPage<\n { _pageId: string } & PageContext,\n typeof pageContextInit\n >(pageContextInit);\n\n // should stop relying on unstable _debugRouteMatches after this issue is closed: https://github.com/vikejs/vike/issues/1112\n const originalPageId =\n (pageContext as any)?._debugRouteMatches?.[0]?.pageId ||\n pageContext._pageId;\n req.bifrostPageId = originalPageId;\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. In prod, it'd be better to use ALB to flip target\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);\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 layoutInfo = req.getLayout?.(reply.getHeaders());\n if (!layoutInfo?.layout) {\n return reply.send(res);\n }\n\n const html = await streamToString(res);\n\n const pageContextInit = {\n urlOriginal: req.url,\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 layout: layoutInfo.layout,\n layoutProps: layoutInfo.layoutProps,\n html,\n },\n };\n const pageContext = await renderPage(pageContextInit);\n return replyWithPage(reply, pageContext);\n },\n },\n });\n};\n"],"mappings":"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","names":[]}
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.39",
4
+ "version": "0.1.0-rc.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -14,12 +14,12 @@
14
14
  "cross-env": "^7.0.3",
15
15
  "fastify": "^4.9.2",
16
16
  "sirv": "^2.0.2",
17
- "vite": "^4.0.3"
17
+ "vite": "^5.1.0"
18
18
  },
19
19
  "peerDependencies": {
20
- "@alignable/bifrost": "0.0.39",
20
+ "@alignable/bifrost": "0.1.0-rc.0",
21
21
  "fastify": "^4.9.2",
22
- "vike": "0.4.168"
22
+ "vike": "0.4.193"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/connect": "^3.4.35",