codeforlife 2.10.3 → 2.10.4
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/server.cjs.js +2 -2
- package/dist/server.cjs.js.map +1 -1
- package/dist/server.es.js +1 -0
- package/dist/server.es.js.map +1 -1
- package/package.json +1 -1
package/dist/server.cjs.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
"use strict";var u=Object.create;var c=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var C=Object.getPrototypeOf,k=Object.prototype.hasOwnProperty;var w=(r,s,e,t)=>{if(s&&typeof s=="object"||typeof s=="function")for(let a of v(s))!k.call(r,a)&&a!==e&&c(r,a,{get:()=>s[a],enumerable:!(t=m(s,a))||t.enumerable});return r};var o=(r,s,e)=>(e=r!=null?u(C(r)):{},w(s||!r||!r.__esModule?c(e,"default",{value:r,enumerable:!0}):e,r));const y=require("memory-cache"),S=require("node:crypto"),
|
|
1
|
+
"use strict";var u=Object.create;var c=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var C=Object.getPrototypeOf,k=Object.prototype.hasOwnProperty;var w=(r,s,e,t)=>{if(s&&typeof s=="object"||typeof s=="function")for(let a of v(s))!k.call(r,a)&&a!==e&&c(r,a,{get:()=>s[a],enumerable:!(t=m(s,a))||t.enumerable});return r};var o=(r,s,e)=>(e=r!=null?u(C(r)):{},w(s||!r||!r.__esModule?c(e,"default",{value:r,enumerable:!0}):e,r));const y=require("memory-cache"),S=require("node:crypto"),f=require("express"),l=require("node:fs/promises"),I=require("node:http");class P{envIsProduction;templateHtml;hostname;mode;port;base;app;server;cache;healthCheckCacheKey;healthCheckCacheTimeout;healthCheckStatusCodes;devtoolsWorkspaceUUID;constructor({mode:s,port:e,base:t}={}){this.envIsProduction=process.env.NODE_ENV==="production",this.templateHtml="",this.hostname=this.envIsProduction?"0.0.0.0":"127.0.0.1",this.mode=s||process.env.MODE||"development",this.port=e||(process.env.PORT?Number(process.env.PORT):this.envIsProduction?8080:5173),this.base=t||process.env.BASE||"/",this.app=f(),this.server=I.createServer(this.app),this.cache=new y.Cache,this.healthCheckCacheKey="health-check",this.healthCheckCacheTimeout=3e4,this.healthCheckStatusCodes={healthy:200,startingUp:503,shuttingDown:503,unhealthy:503,unknown:503},this.devtoolsWorkspaceUUID=S.randomUUID()}getHealthCheck(s){return{healthStatus:"healthy",additionalInfo:"All healthy."}}handleHealthCheck(s,e){let t=this.cache.get(this.healthCheckCacheKey);if(t===null){const a=this.getHealthCheck(s);a.healthStatus!=="healthy"&&console.warn(`health check: ${JSON.stringify(a)}`),t={appId:process.env.APP_ID||"REPLACE_ME",healthStatus:a.healthStatus,lastCheckedTimestamp:new Date().toISOString(),additionalInformation:a.additionalInfo,startupTimestamp:new Date().toISOString(),appVersion:process.env.APP_VERSION||"REPLACE_ME",details:a.details||[]},this.cache.put(this.healthCheckCacheKey,t,this.healthCheckCacheTimeout)}e.status(this.healthCheckStatusCodes[t.healthStatus]).json(t)}async handleServeHtml(s,e,{getRenderAndTemplate:t,onServeError:a}){try{const h=s.originalUrl.replace(this.base,""),[n,p]=await t(h),i=await n(h),d=p.replace("<!--app-head-->",i.head??"").replace("<!--app-html-->",i.html??"");e.status(200).set({"Content-Type":"text/html"}).send(d)}catch(h){if(h instanceof Error){console.error(h.stack);const n=a(h);e.status(500).end(n)}}}handleChromeDevTools(s,e){if(this.envIsProduction)e.status(404).json({});else{const t=process.env.LOCAL_WORKSPACE_PATH;let a,h;t?(a=200,h={workspace:{uuid:this.devtoolsWorkspaceUUID,root:t}}):(a=404,h={error:"Local workspace path not configured."}),e.status(a).json(h)}}async setUpProduction(){const s=(await import("compression")).default,e=(await import("sirv")).default;return this.templateHtml=await l.readFile("./dist/client/index.html","utf-8"),this.app.use(s()),this.app.use(this.base,e("./dist/client",{extensions:[]})),{getRenderAndTemplate:async()=>{const t=(await import("../../../dist/server/entry-server.js")).render,a=this.templateHtml;return[t,a]},onServeError:()=>{}}}async setUpDevelopment(){const{createServer:s}=await import("vite"),e=await s({configFile:"/workspace/frontend/vite.config.ts",server:{middlewareMode:!0,hmr:{server:this.server}},appType:"custom",base:this.base,mode:this.mode});return this.app.use(e.middlewares),{getRenderAndTemplate:async t=>{const a=(await e.ssrLoadModule("/src/entry-server.tsx")).render;let h=await l.readFile("./index.html","utf-8");return h=await e.transformIndexHtml(t,h),[a,h]},onServeError:t=>(e.ssrFixStacktrace(t),t.stack)}}async run(){const s=this.envIsProduction?await this.setUpProduction():await this.setUpDevelopment();this.app.get("/health-check",(e,t)=>{this.handleHealthCheck(e,t)}),this.app.get("/.well-known/appspecific/com.chrome.devtools.json",(e,t)=>{this.handleChromeDevTools(e,t)}),this.app.get("*",async(e,t)=>{await this.handleServeHtml(e,t,s)}),this.server.listen(this.port,this.hostname,()=>{let e=`Server started.
|
|
2
2
|
url: http://${this.hostname}:${this.port}
|
|
3
3
|
environment: ${process.env.NODE_ENV}
|
|
4
4
|
`;this.envIsProduction||(e+=`mode: ${this.mode}
|
|
5
|
-
`),console.log(e)})}}module.exports=
|
|
5
|
+
`),console.log(e)})}}module.exports=P;
|
|
6
6
|
//# sourceMappingURL=server.cjs.js.map
|
package/dist/server.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.cjs.js","sources":["../src/server.ts"],"sourcesContent":["/**\n * © Ocado Group\n * Created on 13/12/2024 at 12:15:05(+00:00).\n *\n * A server for an app in a live environment.\n * Based off: https://github.com/bluwy/create-vite-extra/blob/master/template-ssr-react-ts/server.js\n */\n\nimport { Cache, type CacheClass } from \"memory-cache\"\nimport { type UUID, randomUUID } from \"node:crypto\"\nimport express, { type Express, type Request, type Response } from \"express\"\nimport fs from \"node:fs/promises\"\nimport http from \"node:http\"\n\ntype Mode = \"development\" | \"staging\" | \"production\"\ntype Options = Partial<{\n mode: Mode\n port: number\n base: string\n}>\n\ntype HealthStatus =\n | \"healthy\"\n | \"startingUp\"\n | \"shuttingDown\"\n | \"unhealthy\"\n | \"unknown\"\ntype HealthCheck = {\n healthStatus: HealthStatus\n additionalInfo: string\n details?: Array<{\n name: string\n description: string\n health: HealthStatus\n }>\n}\ntype HealthCheckResponse = {\n appId: string\n healthStatus: HealthStatus\n lastCheckedTimestamp: string\n additionalInformation: string\n startupTimestamp: string\n appVersion: string\n details: Array<{\n name: string\n description: string\n health: HealthStatus\n }>\n}\n\ntype Render = (path: string) => Promise<{ head?: string; html?: string }>\ntype EntryModule = { render: Render }\ntype RenderAndTemplate = [Render, string]\ntype GetRenderAndTemplate = (path: string) => Promise<RenderAndTemplate>\ntype OnServeError = (error: Error) => string | undefined\n\ntype Setup = {\n getRenderAndTemplate: GetRenderAndTemplate\n onServeError: OnServeError\n}\n\nexport default class Server {\n envIsProduction: boolean\n templateHtml: string\n hostname: string\n mode: Mode\n port: number\n base: string\n app: Express\n server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>\n cache: CacheClass<string, any>\n healthCheckCacheKey: string\n healthCheckCacheTimeout: number\n healthCheckStatusCodes: Record<HealthStatus, number>\n devtoolsWorkspaceUUID: UUID\n\n constructor({ mode, port, base }: Options = {}) {\n this.envIsProduction = process.env.NODE_ENV === \"production\"\n this.templateHtml = \"\"\n this.hostname = this.envIsProduction ? \"0.0.0.0\" : \"127.0.0.1\"\n\n this.mode = mode || (process.env.MODE as Mode) || \"development\"\n this.port =\n port ||\n (process.env.PORT\n ? Number(process.env.PORT)\n : this.envIsProduction\n ? 8080\n : 5173)\n this.base = base || process.env.BASE || \"/\"\n\n this.app = express()\n this.server = http.createServer(this.app)\n this.cache = new Cache()\n\n this.healthCheckCacheKey = \"health-check\"\n this.healthCheckCacheTimeout = 30000\n this.healthCheckStatusCodes = {\n // The app is running normally.\n healthy: 200,\n // The app is performing app-specific initialisation which must\n // complete before it will serve normal application requests\n // (perhaps the app is warming a cache or something similar). You\n // only need to use this status if your app will be in a start-up\n // mode for a prolonged period of time.\n startingUp: 503,\n // The app is shutting down. As with startingUp, you only need to\n // use this status if your app takes a prolonged amount of time\n // to shutdown, perhaps because it waits for a long-running\n // process to complete before shutting down.\n shuttingDown: 503,\n // The app is not running normally.\n unhealthy: 503,\n // The app is not able to report its own state.\n unknown: 503,\n }\n\n this.devtoolsWorkspaceUUID = randomUUID()\n }\n\n // @ts-expect-error unused var\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n getHealthCheck(request: Request): HealthCheck {\n return {\n healthStatus: \"healthy\",\n additionalInfo: \"All healthy.\",\n }\n }\n\n handleHealthCheck(request: Request, response: Response): void {\n let value: HealthCheckResponse = this.cache.get(\n this.healthCheckCacheKey,\n ) as HealthCheckResponse\n if (value === null) {\n const healthCheck = this.getHealthCheck(request)\n\n if (healthCheck.healthStatus !== \"healthy\") {\n console.warn(`health check: ${JSON.stringify(healthCheck)}`)\n }\n\n value = {\n appId: process.env.APP_ID || \"REPLACE_ME\",\n healthStatus: healthCheck.healthStatus,\n lastCheckedTimestamp: new Date().toISOString(),\n additionalInformation: healthCheck.additionalInfo,\n startupTimestamp: new Date().toISOString(),\n appVersion: process.env.APP_VERSION || \"REPLACE_ME\",\n details: healthCheck.details || [],\n }\n\n this.cache.put(\n this.healthCheckCacheKey,\n value,\n this.healthCheckCacheTimeout,\n )\n }\n\n response.status(this.healthCheckStatusCodes[value.healthStatus]).json(value)\n }\n\n async handleServeHtml(\n request: Request,\n response: Response,\n { getRenderAndTemplate, onServeError }: Setup,\n ): Promise<void> {\n try {\n const path = request.originalUrl.replace(this.base, \"\")\n\n const [render, template] = await getRenderAndTemplate(path)\n\n const rendered = await render(path)\n\n const html = template\n .replace(`<!--app-head-->`, rendered.head ?? \"\")\n .replace(`<!--app-html-->`, rendered.html ?? \"\")\n\n response.status(200).set({ \"Content-Type\": \"text/html\" }).send(html)\n } catch (error) {\n if (error instanceof Error) {\n console.error(error.stack)\n const body = onServeError(error)\n response.status(500).end(body)\n }\n }\n }\n\n // @ts-expect-error unused var\n handleChromeDevTools(request: Request, response: Response) {\n if (this.envIsProduction) {\n response.status(404).json({})\n } else {\n const localWorkspacePath = process.env.LOCAL_WORKSPACE_PATH\n\n let code: number\n let body: object\n if (localWorkspacePath) {\n code = 200\n body = {\n workspace: {\n uuid: this.devtoolsWorkspaceUUID,\n root: localWorkspacePath,\n },\n }\n } else {\n code = 404\n body = { error: \"Local workspace path not configured.\" }\n }\n\n response.status(code).json(body)\n }\n }\n\n async setUpProduction(): Promise<Setup> {\n const compression = (await import(\"compression\")).default\n const sirv = (await import(\"sirv\")).default\n\n this.templateHtml = await fs.readFile(\"./dist/client/index.html\", \"utf-8\")\n\n this.app.use(compression())\n this.app.use(this.base, sirv(\"./dist/client\", { extensions: [] }))\n\n return {\n getRenderAndTemplate: async () => {\n const render = (\n (await import(\n // @ts-expect-error only present after building installing app.\n \"../../../dist/server/entry-server.js\"\n )) as EntryModule\n ).render\n\n // Use cached template.\n const template = this.templateHtml\n\n return [render, template]\n },\n onServeError: () => undefined,\n }\n }\n\n async setUpDevelopment(): Promise<Setup> {\n const { createServer } = await import(\"vite\")\n\n const vite = await createServer({\n server: {\n middlewareMode: true,\n hmr: { server: this.server },\n },\n appType: \"custom\",\n base: this.base,\n mode: this.mode,\n })\n\n this.app.use(vite.middlewares)\n\n return {\n getRenderAndTemplate: async path => {\n const render = (\n (await vite.ssrLoadModule(\"/src/entry-server.tsx\")) as EntryModule\n ).render\n\n // Always read fresh template.\n let template = await fs.readFile(\"./index.html\", \"utf-8\")\n template = await vite.transformIndexHtml(path, template)\n\n return [render, template]\n },\n onServeError: error => {\n vite.ssrFixStacktrace(error)\n return error.stack\n },\n }\n }\n\n async run() {\n const setup = this.envIsProduction\n ? await this.setUpProduction()\n : await this.setUpDevelopment()\n\n this.app.get(\"/health-check\", (request, response) => {\n this.handleHealthCheck(request, response)\n })\n\n this.app.get(\n \"/.well-known/appspecific/com.chrome.devtools.json\",\n (request, response) => {\n this.handleChromeDevTools(request, response)\n },\n )\n\n this.app.get(\"*\", async (request, response) => {\n await this.handleServeHtml(request, response, setup)\n })\n\n this.server.listen(this.port, this.hostname, () => {\n let startMessage =\n \"Server started.\\n\" +\n `url: http://${this.hostname}:${this.port}\\n` +\n `environment: ${process.env.NODE_ENV}\\n`\n\n if (!this.envIsProduction) startMessage += `mode: ${this.mode}\\n`\n\n console.log(startMessage)\n })\n }\n}\n"],"names":["Server","mode","port","base","express","http","Cache","randomUUID","request","response","value","healthCheck","getRenderAndTemplate","onServeError","path","render","template","rendered","html","error","body","localWorkspacePath","code","compression","sirv","fs","createServer","vite","setup","startMessage"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.cjs.js","sources":["../src/server.ts"],"sourcesContent":["/**\n * © Ocado Group\n * Created on 13/12/2024 at 12:15:05(+00:00).\n *\n * A server for an app in a live environment.\n * Based off: https://github.com/bluwy/create-vite-extra/blob/master/template-ssr-react-ts/server.js\n */\n\nimport { Cache, type CacheClass } from \"memory-cache\"\nimport { type UUID, randomUUID } from \"node:crypto\"\nimport express, { type Express, type Request, type Response } from \"express\"\nimport fs from \"node:fs/promises\"\nimport http from \"node:http\"\n\ntype Mode = \"development\" | \"staging\" | \"production\"\ntype Options = Partial<{\n mode: Mode\n port: number\n base: string\n}>\n\ntype HealthStatus =\n | \"healthy\"\n | \"startingUp\"\n | \"shuttingDown\"\n | \"unhealthy\"\n | \"unknown\"\ntype HealthCheck = {\n healthStatus: HealthStatus\n additionalInfo: string\n details?: Array<{\n name: string\n description: string\n health: HealthStatus\n }>\n}\ntype HealthCheckResponse = {\n appId: string\n healthStatus: HealthStatus\n lastCheckedTimestamp: string\n additionalInformation: string\n startupTimestamp: string\n appVersion: string\n details: Array<{\n name: string\n description: string\n health: HealthStatus\n }>\n}\n\ntype Render = (path: string) => Promise<{ head?: string; html?: string }>\ntype EntryModule = { render: Render }\ntype RenderAndTemplate = [Render, string]\ntype GetRenderAndTemplate = (path: string) => Promise<RenderAndTemplate>\ntype OnServeError = (error: Error) => string | undefined\n\ntype Setup = {\n getRenderAndTemplate: GetRenderAndTemplate\n onServeError: OnServeError\n}\n\nexport default class Server {\n envIsProduction: boolean\n templateHtml: string\n hostname: string\n mode: Mode\n port: number\n base: string\n app: Express\n server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>\n cache: CacheClass<string, any>\n healthCheckCacheKey: string\n healthCheckCacheTimeout: number\n healthCheckStatusCodes: Record<HealthStatus, number>\n devtoolsWorkspaceUUID: UUID\n\n constructor({ mode, port, base }: Options = {}) {\n this.envIsProduction = process.env.NODE_ENV === \"production\"\n this.templateHtml = \"\"\n this.hostname = this.envIsProduction ? \"0.0.0.0\" : \"127.0.0.1\"\n\n this.mode = mode || (process.env.MODE as Mode) || \"development\"\n this.port =\n port ||\n (process.env.PORT\n ? Number(process.env.PORT)\n : this.envIsProduction\n ? 8080\n : 5173)\n this.base = base || process.env.BASE || \"/\"\n\n this.app = express()\n this.server = http.createServer(this.app)\n this.cache = new Cache()\n\n this.healthCheckCacheKey = \"health-check\"\n this.healthCheckCacheTimeout = 30000\n this.healthCheckStatusCodes = {\n // The app is running normally.\n healthy: 200,\n // The app is performing app-specific initialisation which must\n // complete before it will serve normal application requests\n // (perhaps the app is warming a cache or something similar). You\n // only need to use this status if your app will be in a start-up\n // mode for a prolonged period of time.\n startingUp: 503,\n // The app is shutting down. As with startingUp, you only need to\n // use this status if your app takes a prolonged amount of time\n // to shutdown, perhaps because it waits for a long-running\n // process to complete before shutting down.\n shuttingDown: 503,\n // The app is not running normally.\n unhealthy: 503,\n // The app is not able to report its own state.\n unknown: 503,\n }\n\n this.devtoolsWorkspaceUUID = randomUUID()\n }\n\n // @ts-expect-error unused var\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n getHealthCheck(request: Request): HealthCheck {\n return {\n healthStatus: \"healthy\",\n additionalInfo: \"All healthy.\",\n }\n }\n\n handleHealthCheck(request: Request, response: Response): void {\n let value: HealthCheckResponse = this.cache.get(\n this.healthCheckCacheKey,\n ) as HealthCheckResponse\n if (value === null) {\n const healthCheck = this.getHealthCheck(request)\n\n if (healthCheck.healthStatus !== \"healthy\") {\n console.warn(`health check: ${JSON.stringify(healthCheck)}`)\n }\n\n value = {\n appId: process.env.APP_ID || \"REPLACE_ME\",\n healthStatus: healthCheck.healthStatus,\n lastCheckedTimestamp: new Date().toISOString(),\n additionalInformation: healthCheck.additionalInfo,\n startupTimestamp: new Date().toISOString(),\n appVersion: process.env.APP_VERSION || \"REPLACE_ME\",\n details: healthCheck.details || [],\n }\n\n this.cache.put(\n this.healthCheckCacheKey,\n value,\n this.healthCheckCacheTimeout,\n )\n }\n\n response.status(this.healthCheckStatusCodes[value.healthStatus]).json(value)\n }\n\n async handleServeHtml(\n request: Request,\n response: Response,\n { getRenderAndTemplate, onServeError }: Setup,\n ): Promise<void> {\n try {\n const path = request.originalUrl.replace(this.base, \"\")\n\n const [render, template] = await getRenderAndTemplate(path)\n\n const rendered = await render(path)\n\n const html = template\n .replace(`<!--app-head-->`, rendered.head ?? \"\")\n .replace(`<!--app-html-->`, rendered.html ?? \"\")\n\n response.status(200).set({ \"Content-Type\": \"text/html\" }).send(html)\n } catch (error) {\n if (error instanceof Error) {\n console.error(error.stack)\n const body = onServeError(error)\n response.status(500).end(body)\n }\n }\n }\n\n // @ts-expect-error unused var\n handleChromeDevTools(request: Request, response: Response) {\n if (this.envIsProduction) {\n response.status(404).json({})\n } else {\n const localWorkspacePath = process.env.LOCAL_WORKSPACE_PATH\n\n let code: number\n let body: object\n if (localWorkspacePath) {\n code = 200\n body = {\n workspace: {\n uuid: this.devtoolsWorkspaceUUID,\n root: localWorkspacePath,\n },\n }\n } else {\n code = 404\n body = { error: \"Local workspace path not configured.\" }\n }\n\n response.status(code).json(body)\n }\n }\n\n async setUpProduction(): Promise<Setup> {\n const compression = (await import(\"compression\")).default\n const sirv = (await import(\"sirv\")).default\n\n this.templateHtml = await fs.readFile(\"./dist/client/index.html\", \"utf-8\")\n\n this.app.use(compression())\n this.app.use(this.base, sirv(\"./dist/client\", { extensions: [] }))\n\n return {\n getRenderAndTemplate: async () => {\n const render = (\n (await import(\n // @ts-expect-error only present after building installing app.\n \"../../../dist/server/entry-server.js\"\n )) as EntryModule\n ).render\n\n // Use cached template.\n const template = this.templateHtml\n\n return [render, template]\n },\n onServeError: () => undefined,\n }\n }\n\n async setUpDevelopment(): Promise<Setup> {\n const { createServer } = await import(\"vite\")\n\n const vite = await createServer({\n configFile: \"/workspace/frontend/vite.config.ts\",\n server: {\n middlewareMode: true,\n hmr: { server: this.server },\n },\n appType: \"custom\",\n base: this.base,\n mode: this.mode,\n })\n\n this.app.use(vite.middlewares)\n\n return {\n getRenderAndTemplate: async path => {\n const render = (\n (await vite.ssrLoadModule(\"/src/entry-server.tsx\")) as EntryModule\n ).render\n\n // Always read fresh template.\n let template = await fs.readFile(\"./index.html\", \"utf-8\")\n template = await vite.transformIndexHtml(path, template)\n\n return [render, template]\n },\n onServeError: error => {\n vite.ssrFixStacktrace(error)\n return error.stack\n },\n }\n }\n\n async run() {\n const setup = this.envIsProduction\n ? await this.setUpProduction()\n : await this.setUpDevelopment()\n\n this.app.get(\"/health-check\", (request, response) => {\n this.handleHealthCheck(request, response)\n })\n\n this.app.get(\n \"/.well-known/appspecific/com.chrome.devtools.json\",\n (request, response) => {\n this.handleChromeDevTools(request, response)\n },\n )\n\n this.app.get(\"*\", async (request, response) => {\n await this.handleServeHtml(request, response, setup)\n })\n\n this.server.listen(this.port, this.hostname, () => {\n let startMessage =\n \"Server started.\\n\" +\n `url: http://${this.hostname}:${this.port}\\n` +\n `environment: ${process.env.NODE_ENV}\\n`\n\n if (!this.envIsProduction) startMessage += `mode: ${this.mode}\\n`\n\n console.log(startMessage)\n })\n }\n}\n"],"names":["Server","mode","port","base","express","http","Cache","randomUUID","request","response","value","healthCheck","getRenderAndTemplate","onServeError","path","render","template","rendered","html","error","body","localWorkspacePath","code","compression","sirv","fs","createServer","vite","setup","startMessage"],"mappings":"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"}
|
package/dist/server.es.js
CHANGED
package/dist/server.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.es.js","sources":["../src/server.ts"],"sourcesContent":["/**\n * © Ocado Group\n * Created on 13/12/2024 at 12:15:05(+00:00).\n *\n * A server for an app in a live environment.\n * Based off: https://github.com/bluwy/create-vite-extra/blob/master/template-ssr-react-ts/server.js\n */\n\nimport { Cache, type CacheClass } from \"memory-cache\"\nimport { type UUID, randomUUID } from \"node:crypto\"\nimport express, { type Express, type Request, type Response } from \"express\"\nimport fs from \"node:fs/promises\"\nimport http from \"node:http\"\n\ntype Mode = \"development\" | \"staging\" | \"production\"\ntype Options = Partial<{\n mode: Mode\n port: number\n base: string\n}>\n\ntype HealthStatus =\n | \"healthy\"\n | \"startingUp\"\n | \"shuttingDown\"\n | \"unhealthy\"\n | \"unknown\"\ntype HealthCheck = {\n healthStatus: HealthStatus\n additionalInfo: string\n details?: Array<{\n name: string\n description: string\n health: HealthStatus\n }>\n}\ntype HealthCheckResponse = {\n appId: string\n healthStatus: HealthStatus\n lastCheckedTimestamp: string\n additionalInformation: string\n startupTimestamp: string\n appVersion: string\n details: Array<{\n name: string\n description: string\n health: HealthStatus\n }>\n}\n\ntype Render = (path: string) => Promise<{ head?: string; html?: string }>\ntype EntryModule = { render: Render }\ntype RenderAndTemplate = [Render, string]\ntype GetRenderAndTemplate = (path: string) => Promise<RenderAndTemplate>\ntype OnServeError = (error: Error) => string | undefined\n\ntype Setup = {\n getRenderAndTemplate: GetRenderAndTemplate\n onServeError: OnServeError\n}\n\nexport default class Server {\n envIsProduction: boolean\n templateHtml: string\n hostname: string\n mode: Mode\n port: number\n base: string\n app: Express\n server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>\n cache: CacheClass<string, any>\n healthCheckCacheKey: string\n healthCheckCacheTimeout: number\n healthCheckStatusCodes: Record<HealthStatus, number>\n devtoolsWorkspaceUUID: UUID\n\n constructor({ mode, port, base }: Options = {}) {\n this.envIsProduction = process.env.NODE_ENV === \"production\"\n this.templateHtml = \"\"\n this.hostname = this.envIsProduction ? \"0.0.0.0\" : \"127.0.0.1\"\n\n this.mode = mode || (process.env.MODE as Mode) || \"development\"\n this.port =\n port ||\n (process.env.PORT\n ? Number(process.env.PORT)\n : this.envIsProduction\n ? 8080\n : 5173)\n this.base = base || process.env.BASE || \"/\"\n\n this.app = express()\n this.server = http.createServer(this.app)\n this.cache = new Cache()\n\n this.healthCheckCacheKey = \"health-check\"\n this.healthCheckCacheTimeout = 30000\n this.healthCheckStatusCodes = {\n // The app is running normally.\n healthy: 200,\n // The app is performing app-specific initialisation which must\n // complete before it will serve normal application requests\n // (perhaps the app is warming a cache or something similar). You\n // only need to use this status if your app will be in a start-up\n // mode for a prolonged period of time.\n startingUp: 503,\n // The app is shutting down. As with startingUp, you only need to\n // use this status if your app takes a prolonged amount of time\n // to shutdown, perhaps because it waits for a long-running\n // process to complete before shutting down.\n shuttingDown: 503,\n // The app is not running normally.\n unhealthy: 503,\n // The app is not able to report its own state.\n unknown: 503,\n }\n\n this.devtoolsWorkspaceUUID = randomUUID()\n }\n\n // @ts-expect-error unused var\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n getHealthCheck(request: Request): HealthCheck {\n return {\n healthStatus: \"healthy\",\n additionalInfo: \"All healthy.\",\n }\n }\n\n handleHealthCheck(request: Request, response: Response): void {\n let value: HealthCheckResponse = this.cache.get(\n this.healthCheckCacheKey,\n ) as HealthCheckResponse\n if (value === null) {\n const healthCheck = this.getHealthCheck(request)\n\n if (healthCheck.healthStatus !== \"healthy\") {\n console.warn(`health check: ${JSON.stringify(healthCheck)}`)\n }\n\n value = {\n appId: process.env.APP_ID || \"REPLACE_ME\",\n healthStatus: healthCheck.healthStatus,\n lastCheckedTimestamp: new Date().toISOString(),\n additionalInformation: healthCheck.additionalInfo,\n startupTimestamp: new Date().toISOString(),\n appVersion: process.env.APP_VERSION || \"REPLACE_ME\",\n details: healthCheck.details || [],\n }\n\n this.cache.put(\n this.healthCheckCacheKey,\n value,\n this.healthCheckCacheTimeout,\n )\n }\n\n response.status(this.healthCheckStatusCodes[value.healthStatus]).json(value)\n }\n\n async handleServeHtml(\n request: Request,\n response: Response,\n { getRenderAndTemplate, onServeError }: Setup,\n ): Promise<void> {\n try {\n const path = request.originalUrl.replace(this.base, \"\")\n\n const [render, template] = await getRenderAndTemplate(path)\n\n const rendered = await render(path)\n\n const html = template\n .replace(`<!--app-head-->`, rendered.head ?? \"\")\n .replace(`<!--app-html-->`, rendered.html ?? \"\")\n\n response.status(200).set({ \"Content-Type\": \"text/html\" }).send(html)\n } catch (error) {\n if (error instanceof Error) {\n console.error(error.stack)\n const body = onServeError(error)\n response.status(500).end(body)\n }\n }\n }\n\n // @ts-expect-error unused var\n handleChromeDevTools(request: Request, response: Response) {\n if (this.envIsProduction) {\n response.status(404).json({})\n } else {\n const localWorkspacePath = process.env.LOCAL_WORKSPACE_PATH\n\n let code: number\n let body: object\n if (localWorkspacePath) {\n code = 200\n body = {\n workspace: {\n uuid: this.devtoolsWorkspaceUUID,\n root: localWorkspacePath,\n },\n }\n } else {\n code = 404\n body = { error: \"Local workspace path not configured.\" }\n }\n\n response.status(code).json(body)\n }\n }\n\n async setUpProduction(): Promise<Setup> {\n const compression = (await import(\"compression\")).default\n const sirv = (await import(\"sirv\")).default\n\n this.templateHtml = await fs.readFile(\"./dist/client/index.html\", \"utf-8\")\n\n this.app.use(compression())\n this.app.use(this.base, sirv(\"./dist/client\", { extensions: [] }))\n\n return {\n getRenderAndTemplate: async () => {\n const render = (\n (await import(\n // @ts-expect-error only present after building installing app.\n \"../../../dist/server/entry-server.js\"\n )) as EntryModule\n ).render\n\n // Use cached template.\n const template = this.templateHtml\n\n return [render, template]\n },\n onServeError: () => undefined,\n }\n }\n\n async setUpDevelopment(): Promise<Setup> {\n const { createServer } = await import(\"vite\")\n\n const vite = await createServer({\n server: {\n middlewareMode: true,\n hmr: { server: this.server },\n },\n appType: \"custom\",\n base: this.base,\n mode: this.mode,\n })\n\n this.app.use(vite.middlewares)\n\n return {\n getRenderAndTemplate: async path => {\n const render = (\n (await vite.ssrLoadModule(\"/src/entry-server.tsx\")) as EntryModule\n ).render\n\n // Always read fresh template.\n let template = await fs.readFile(\"./index.html\", \"utf-8\")\n template = await vite.transformIndexHtml(path, template)\n\n return [render, template]\n },\n onServeError: error => {\n vite.ssrFixStacktrace(error)\n return error.stack\n },\n }\n }\n\n async run() {\n const setup = this.envIsProduction\n ? await this.setUpProduction()\n : await this.setUpDevelopment()\n\n this.app.get(\"/health-check\", (request, response) => {\n this.handleHealthCheck(request, response)\n })\n\n this.app.get(\n \"/.well-known/appspecific/com.chrome.devtools.json\",\n (request, response) => {\n this.handleChromeDevTools(request, response)\n },\n )\n\n this.app.get(\"*\", async (request, response) => {\n await this.handleServeHtml(request, response, setup)\n })\n\n this.server.listen(this.port, this.hostname, () => {\n let startMessage =\n \"Server started.\\n\" +\n `url: http://${this.hostname}:${this.port}\\n` +\n `environment: ${process.env.NODE_ENV}\\n`\n\n if (!this.envIsProduction) startMessage += `mode: ${this.mode}\\n`\n\n console.log(startMessage)\n })\n }\n}\n"],"names":["Server","mode","port","base","express","http","Cache","randomUUID","request","response","value","healthCheck","getRenderAndTemplate","onServeError","path","render","template","rendered","html","error","body","localWorkspacePath","code","compression","sirv","fs","createServer","vite","setup","startMessage"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.es.js","sources":["../src/server.ts"],"sourcesContent":["/**\n * © Ocado Group\n * Created on 13/12/2024 at 12:15:05(+00:00).\n *\n * A server for an app in a live environment.\n * Based off: https://github.com/bluwy/create-vite-extra/blob/master/template-ssr-react-ts/server.js\n */\n\nimport { Cache, type CacheClass } from \"memory-cache\"\nimport { type UUID, randomUUID } from \"node:crypto\"\nimport express, { type Express, type Request, type Response } from \"express\"\nimport fs from \"node:fs/promises\"\nimport http from \"node:http\"\n\ntype Mode = \"development\" | \"staging\" | \"production\"\ntype Options = Partial<{\n mode: Mode\n port: number\n base: string\n}>\n\ntype HealthStatus =\n | \"healthy\"\n | \"startingUp\"\n | \"shuttingDown\"\n | \"unhealthy\"\n | \"unknown\"\ntype HealthCheck = {\n healthStatus: HealthStatus\n additionalInfo: string\n details?: Array<{\n name: string\n description: string\n health: HealthStatus\n }>\n}\ntype HealthCheckResponse = {\n appId: string\n healthStatus: HealthStatus\n lastCheckedTimestamp: string\n additionalInformation: string\n startupTimestamp: string\n appVersion: string\n details: Array<{\n name: string\n description: string\n health: HealthStatus\n }>\n}\n\ntype Render = (path: string) => Promise<{ head?: string; html?: string }>\ntype EntryModule = { render: Render }\ntype RenderAndTemplate = [Render, string]\ntype GetRenderAndTemplate = (path: string) => Promise<RenderAndTemplate>\ntype OnServeError = (error: Error) => string | undefined\n\ntype Setup = {\n getRenderAndTemplate: GetRenderAndTemplate\n onServeError: OnServeError\n}\n\nexport default class Server {\n envIsProduction: boolean\n templateHtml: string\n hostname: string\n mode: Mode\n port: number\n base: string\n app: Express\n server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>\n cache: CacheClass<string, any>\n healthCheckCacheKey: string\n healthCheckCacheTimeout: number\n healthCheckStatusCodes: Record<HealthStatus, number>\n devtoolsWorkspaceUUID: UUID\n\n constructor({ mode, port, base }: Options = {}) {\n this.envIsProduction = process.env.NODE_ENV === \"production\"\n this.templateHtml = \"\"\n this.hostname = this.envIsProduction ? \"0.0.0.0\" : \"127.0.0.1\"\n\n this.mode = mode || (process.env.MODE as Mode) || \"development\"\n this.port =\n port ||\n (process.env.PORT\n ? Number(process.env.PORT)\n : this.envIsProduction\n ? 8080\n : 5173)\n this.base = base || process.env.BASE || \"/\"\n\n this.app = express()\n this.server = http.createServer(this.app)\n this.cache = new Cache()\n\n this.healthCheckCacheKey = \"health-check\"\n this.healthCheckCacheTimeout = 30000\n this.healthCheckStatusCodes = {\n // The app is running normally.\n healthy: 200,\n // The app is performing app-specific initialisation which must\n // complete before it will serve normal application requests\n // (perhaps the app is warming a cache or something similar). You\n // only need to use this status if your app will be in a start-up\n // mode for a prolonged period of time.\n startingUp: 503,\n // The app is shutting down. As with startingUp, you only need to\n // use this status if your app takes a prolonged amount of time\n // to shutdown, perhaps because it waits for a long-running\n // process to complete before shutting down.\n shuttingDown: 503,\n // The app is not running normally.\n unhealthy: 503,\n // The app is not able to report its own state.\n unknown: 503,\n }\n\n this.devtoolsWorkspaceUUID = randomUUID()\n }\n\n // @ts-expect-error unused var\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n getHealthCheck(request: Request): HealthCheck {\n return {\n healthStatus: \"healthy\",\n additionalInfo: \"All healthy.\",\n }\n }\n\n handleHealthCheck(request: Request, response: Response): void {\n let value: HealthCheckResponse = this.cache.get(\n this.healthCheckCacheKey,\n ) as HealthCheckResponse\n if (value === null) {\n const healthCheck = this.getHealthCheck(request)\n\n if (healthCheck.healthStatus !== \"healthy\") {\n console.warn(`health check: ${JSON.stringify(healthCheck)}`)\n }\n\n value = {\n appId: process.env.APP_ID || \"REPLACE_ME\",\n healthStatus: healthCheck.healthStatus,\n lastCheckedTimestamp: new Date().toISOString(),\n additionalInformation: healthCheck.additionalInfo,\n startupTimestamp: new Date().toISOString(),\n appVersion: process.env.APP_VERSION || \"REPLACE_ME\",\n details: healthCheck.details || [],\n }\n\n this.cache.put(\n this.healthCheckCacheKey,\n value,\n this.healthCheckCacheTimeout,\n )\n }\n\n response.status(this.healthCheckStatusCodes[value.healthStatus]).json(value)\n }\n\n async handleServeHtml(\n request: Request,\n response: Response,\n { getRenderAndTemplate, onServeError }: Setup,\n ): Promise<void> {\n try {\n const path = request.originalUrl.replace(this.base, \"\")\n\n const [render, template] = await getRenderAndTemplate(path)\n\n const rendered = await render(path)\n\n const html = template\n .replace(`<!--app-head-->`, rendered.head ?? \"\")\n .replace(`<!--app-html-->`, rendered.html ?? \"\")\n\n response.status(200).set({ \"Content-Type\": \"text/html\" }).send(html)\n } catch (error) {\n if (error instanceof Error) {\n console.error(error.stack)\n const body = onServeError(error)\n response.status(500).end(body)\n }\n }\n }\n\n // @ts-expect-error unused var\n handleChromeDevTools(request: Request, response: Response) {\n if (this.envIsProduction) {\n response.status(404).json({})\n } else {\n const localWorkspacePath = process.env.LOCAL_WORKSPACE_PATH\n\n let code: number\n let body: object\n if (localWorkspacePath) {\n code = 200\n body = {\n workspace: {\n uuid: this.devtoolsWorkspaceUUID,\n root: localWorkspacePath,\n },\n }\n } else {\n code = 404\n body = { error: \"Local workspace path not configured.\" }\n }\n\n response.status(code).json(body)\n }\n }\n\n async setUpProduction(): Promise<Setup> {\n const compression = (await import(\"compression\")).default\n const sirv = (await import(\"sirv\")).default\n\n this.templateHtml = await fs.readFile(\"./dist/client/index.html\", \"utf-8\")\n\n this.app.use(compression())\n this.app.use(this.base, sirv(\"./dist/client\", { extensions: [] }))\n\n return {\n getRenderAndTemplate: async () => {\n const render = (\n (await import(\n // @ts-expect-error only present after building installing app.\n \"../../../dist/server/entry-server.js\"\n )) as EntryModule\n ).render\n\n // Use cached template.\n const template = this.templateHtml\n\n return [render, template]\n },\n onServeError: () => undefined,\n }\n }\n\n async setUpDevelopment(): Promise<Setup> {\n const { createServer } = await import(\"vite\")\n\n const vite = await createServer({\n configFile: \"/workspace/frontend/vite.config.ts\",\n server: {\n middlewareMode: true,\n hmr: { server: this.server },\n },\n appType: \"custom\",\n base: this.base,\n mode: this.mode,\n })\n\n this.app.use(vite.middlewares)\n\n return {\n getRenderAndTemplate: async path => {\n const render = (\n (await vite.ssrLoadModule(\"/src/entry-server.tsx\")) as EntryModule\n ).render\n\n // Always read fresh template.\n let template = await fs.readFile(\"./index.html\", \"utf-8\")\n template = await vite.transformIndexHtml(path, template)\n\n return [render, template]\n },\n onServeError: error => {\n vite.ssrFixStacktrace(error)\n return error.stack\n },\n }\n }\n\n async run() {\n const setup = this.envIsProduction\n ? await this.setUpProduction()\n : await this.setUpDevelopment()\n\n this.app.get(\"/health-check\", (request, response) => {\n this.handleHealthCheck(request, response)\n })\n\n this.app.get(\n \"/.well-known/appspecific/com.chrome.devtools.json\",\n (request, response) => {\n this.handleChromeDevTools(request, response)\n },\n )\n\n this.app.get(\"*\", async (request, response) => {\n await this.handleServeHtml(request, response, setup)\n })\n\n this.server.listen(this.port, this.hostname, () => {\n let startMessage =\n \"Server started.\\n\" +\n `url: http://${this.hostname}:${this.port}\\n` +\n `environment: ${process.env.NODE_ENV}\\n`\n\n if (!this.envIsProduction) startMessage += `mode: ${this.mode}\\n`\n\n console.log(startMessage)\n })\n }\n}\n"],"names":["Server","mode","port","base","express","http","Cache","randomUUID","request","response","value","healthCheck","getRenderAndTemplate","onServeError","path","render","template","rendered","html","error","body","localWorkspacePath","code","compression","sirv","fs","createServer","vite","setup","startMessage"],"mappings":"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"}
|