@trackunit/iris-app-build-utilities 1.12.16 → 1.12.18

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/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ ## 1.12.18 (2026-02-02)
2
+
3
+ ### 🩹 Fixes
4
+
5
+ - **iris-app-build-utilities:** handle path extraction with and without /invoke prefix ([737ddc15d65](https://github.com/Trackunit/manager/commit/737ddc15d65))
6
+
7
+ ### ❤️ Thank You
8
+
9
+ - kla-trackunit
10
+
11
+ ## 1.12.17 (2026-02-02)
12
+
13
+ ### 🧱 Updated Dependencies
14
+
15
+ - Updated iris-app-api to 1.14.16
16
+ - Updated shared-utils to 1.13.17
17
+
1
18
  ## 1.12.16 (2026-01-30)
2
19
 
3
20
  ### 🧱 Updated Dependencies
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/iris-app-build-utilities",
3
- "version": "1.12.16",
3
+ "version": "1.12.18",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "repository": "https://github.com/Trackunit/manager",
6
6
  "engines": {
@@ -17,9 +17,9 @@
17
17
  "tslib": "^2.6.2",
18
18
  "csp-header": "^5.2.1",
19
19
  "@rspack/core": "1.6.7",
20
- "@trackunit/iris-app-api": "1.14.15",
20
+ "@trackunit/iris-app-api": "1.14.16",
21
21
  "@nx/devkit": "22.0.4",
22
- "@trackunit/shared-utils": "1.13.16",
22
+ "@trackunit/shared-utils": "1.13.17",
23
23
  "http-proxy-middleware": "3.0.5",
24
24
  "pacote": "^21.0.4",
25
25
  "semver": "7.5.4"
@@ -10,8 +10,13 @@ const parseExtensionIdFromPath = (path) => {
10
10
  };
11
11
  /** Extracts the endpoint path from an invoke URL. Format: /invoke/@{org}/{app}/{extension-id}/{endpoint} */
12
12
  const extractEndpointPath = (path) => {
13
- const match = path.match(/^\/invoke\/@[^/]+\/[^/]+\/[^/]+(.*)$/);
14
- return match?.[1] || "/";
13
+ // Try with /invoke prefix first (for originalUrl), then without (for req.url after Express mount strips it)
14
+ const matchWithInvoke = path.match(/^\/invoke\/@[^/]+\/[^/]+\/[^/]+(.*)$/);
15
+ if (matchWithInvoke?.[1] !== undefined) {
16
+ return matchWithInvoke[1] || "/";
17
+ }
18
+ const matchWithoutInvoke = path.match(/^\/@[^/]+\/[^/]+\/[^/]+(.*)$/);
19
+ return matchWithoutInvoke?.[1] || "/";
15
20
  };
16
21
  const CORS_ALLOWED_HEADERS = "X-Requested-With, baggage, content-type, Authorization, sentry-trace, session-id, commit-number, x-trackunitappversion";
17
22
  const DEFAULT_ORIGIN = "https://dev.manager.trackunit.com";
@@ -1 +1 @@
1
- {"version":3,"file":"getIrisAppDevServer.js","sourceRoot":"","sources":["../../../../../libs/iris-app-sdk/iris-app-build-utilities/src/getIrisAppDevServer.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DevServer as RspackDevServer } from \"@rspack/core\";\nimport { createProxyMiddleware } from \"http-proxy-middleware\";\nimport type { Configuration as WebpackDevServerConfiguration } from \"webpack-dev-server\";\nimport { getAvailablePort } from \"./getAvailablePort\";\nimport type { ServerlessPortMap } from \"./spawnServerlessExtensions\";\n\nexport interface DevServerOptions {\n serverlessPortMap?: ServerlessPortMap;\n}\n\ntype MiddlewareRequest = {\n url?: string;\n originalUrl?: string;\n method?: string;\n headers: { origin?: string } & Record<string, string | undefined>;\n};\n\ntype MiddlewareResponse = {\n header: (name: string, value: string) => void;\n writeHead: (status: number, headers?: Record<string, string>) => void;\n end: (body?: string) => void;\n send: (body: unknown) => void;\n status: (code: number) => void;\n};\n\ntype MiddlewareNext = () => void;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- http-proxy-middleware types are complex\ntype ProxyHandler = (req: any, res: any, next: MiddlewareNext) => void;\n\ntype DevServerApp = { use: (path: string, handler: unknown) => void };\n\ntype MiddlewareEntry = { name?: string; path?: string; middleware: unknown };\n\n/** Parses the extension ID from an invoke URL path. Format: /invoke/@{org}/{app}/{extension-id}/{endpoint} */\nconst parseExtensionIdFromPath = (path: string): string | undefined => {\n const match = path.match(/^\\/invoke\\/@[^/]+\\/[^/]+\\/([^/]+)/);\n return match?.[1];\n};\n\n/** Extracts the endpoint path from an invoke URL. Format: /invoke/@{org}/{app}/{extension-id}/{endpoint} */\nconst extractEndpointPath = (path: string): string => {\n const match = path.match(/^\\/invoke\\/@[^/]+\\/[^/]+\\/[^/]+(.*)$/);\n return match?.[1] || \"/\";\n};\n\nconst CORS_ALLOWED_HEADERS =\n \"X-Requested-With, baggage, content-type, Authorization, sentry-trace, session-id, commit-number, x-trackunitappversion\";\n\nconst DEFAULT_ORIGIN = \"https://dev.manager.trackunit.com\";\n\nconst getOrigin = (request: { headers: { origin?: string } }): string => request.headers.origin || DEFAULT_ORIGIN;\n\n/** Sets CORS headers on the response. */\nconst setCorsHeaders = (response: Pick<MiddlewareResponse, \"header\">, origin: string): void => {\n response.header(\"Access-Control-Allow-Origin\", origin);\n response.header(\"Access-Control-Allow-Headers\", CORS_ALLOWED_HEADERS);\n};\n\n/** Creates a proxy for the remote Iris platform (used when extension is not running locally). */\nconst createRemoteIrisProxy = (): ProxyHandler =>\n createProxyMiddleware({\n target: \"https://dev.iris.trackunit.app\",\n changeOrigin: true,\n secure: true,\n });\n\n/** Creates a proxy for a local serverless extension. */\nconst createLocalExtensionProxy = (target: string, extensionId: string): ProxyHandler =>\n createProxyMiddleware({\n target,\n changeOrigin: true,\n pathRewrite: path => extractEndpointPath(path),\n on: {\n error: (err, _proxyReq, proxyRes) => {\n // eslint-disable-next-line no-console\n console.error(`[Proxy] Error proxying to ${extensionId}:`, err.message);\n if (\"writeHead\" in proxyRes) {\n proxyRes.writeHead(502, { \"Content-Type\": \"application/json\" });\n proxyRes.end(JSON.stringify({ error: `Serverless extension ${extensionId} unavailable` }));\n }\n },\n },\n });\n\n/** Gets or creates a cached proxy for a local extension. */\nconst getOrCreateLocalProxy = (cache: Map<string, ProxyHandler>, extensionId: string, port: number): ProxyHandler => {\n const existing = cache.get(extensionId);\n if (existing !== undefined) {\n return existing;\n }\n\n const target = `http://localhost:${port}`;\n const newProxy = createLocalExtensionProxy(target, extensionId);\n cache.set(extensionId, newProxy);\n return newProxy;\n};\n\n/** Creates the /invoke proxy middleware that routes to local or remote extensions. */\nconst createInvokeProxyHandler = (\n serverlessPortMap: ServerlessPortMap | undefined,\n localProxyCache: Map<string, ProxyHandler>,\n remoteProxy: ProxyHandler\n) => {\n return (req: MiddlewareRequest, res: MiddlewareResponse, next: MiddlewareNext): void => {\n const fullPath = req.originalUrl ?? `/invoke${req.url ?? \"\"}`;\n const extensionId = parseExtensionIdFromPath(fullPath);\n const localPort = extensionId !== undefined ? serverlessPortMap?.get(extensionId) : undefined;\n\n if (localPort !== undefined && extensionId !== undefined) {\n const endpointPath = extractEndpointPath(fullPath);\n // eslint-disable-next-line no-console\n console.log(`[Proxy] ${fullPath} -> http://localhost:${localPort}${endpointPath}`);\n\n const proxy = getOrCreateLocalProxy(localProxyCache, extensionId, localPort);\n proxy(req, res, next);\n } else {\n remoteProxy(req, res, next);\n }\n };\n};\n\n/** CORS preflight middleware - responds to non-GET requests with allowed methods. */\nconst corsPreflightMiddleware = (\n req: Pick<MiddlewareRequest, \"method\">,\n res: Pick<MiddlewareResponse, \"end\">,\n next: MiddlewareNext\n): void => {\n if (req.method === \"GET\") {\n next();\n } else {\n res.end(\"GET, HEAD\");\n }\n};\n\n/** CORS origin header middleware - sets Access-Control-Allow-Origin on all requests. */\nconst createCorsOriginHandler = () => {\n return (\n request: Pick<MiddlewareRequest, \"headers\">,\n response: Pick<MiddlewareResponse, \"header\">,\n next: MiddlewareNext\n ): void => {\n response.header(\"Access-Control-Allow-Origin\", getOrigin(request));\n next();\n };\n};\n\n/** Creates the /manifestAndToken endpoint handler. */\nconst createManifestAndTokenHandler = (port: number) => {\n return async (\n request: Pick<MiddlewareRequest, \"headers\">,\n response: Pick<MiddlewareResponse, \"header\" | \"send\" | \"status\">\n ): Promise<void> => {\n setCorsHeaders(response, getOrigin(request));\n try {\n const resp = await fetch(`http://localhost:${port}/manifest.json`);\n const body = await resp.json();\n response.send({ manifest: body });\n } catch (e) {\n // eslint-disable-next-line no-console\n console.error(\"ERROR: \", e);\n response.status(500);\n response.send(e);\n }\n };\n};\n\n/** Creates the /extensionloader.js endpoint handler. */\nconst createExtensionLoaderHandler = () => {\n return async (\n request: Pick<MiddlewareRequest, \"headers\">,\n response: Pick<MiddlewareResponse, \"header\" | \"send\" | \"status\">\n ): Promise<void> => {\n setCorsHeaders(response, getOrigin(request));\n try {\n const url =\n process.env.LOCAL === \"true\"\n ? \"http://localhost:3000/extensionloader.js\"\n : \"https://iris.trackunit.app/extensionloader.js\";\n const resp = await fetch(url);\n const body = await resp.text();\n response.send(body);\n } catch (e) {\n // eslint-disable-next-line no-console\n console.error(\"ERROR: \", e);\n response.status(500);\n response.send(e);\n }\n };\n};\n\n/** Registers all middlewares on the dev server app. */\nconst registerMiddlewares = (\n app: DevServerApp,\n middlewares: Array<MiddlewareEntry>,\n port: number,\n options: DevServerOptions\n): Array<MiddlewareEntry> => {\n const localProxyCache = new Map<string, ProxyHandler>();\n const remoteProxy = createRemoteIrisProxy();\n\n // Register /invoke proxy\n app.use(\"/invoke\", createInvokeProxyHandler(options.serverlessPortMap, localProxyCache, remoteProxy));\n\n // Register CORS origin handler on all routes\n app.use(\"/\", createCorsOriginHandler());\n\n // Register endpoint handlers\n app.use(\"/manifestAndToken\", createManifestAndTokenHandler(port));\n app.use(\"/extensionloader.js\", createExtensionLoaderHandler());\n\n // Add CORS preflight to middleware array\n middlewares.push({\n name: \"cors-preflight\",\n path: \"/\",\n middleware: corsPreflightMiddleware,\n });\n\n return middlewares;\n};\n\n/** Creates the base Iris App dev server configuration. */\nconst createIrisAppDevServerConfig = async (port: number, options: DevServerOptions = {}) => ({\n port,\n historyApiFallback: true,\n headers: {\n \"Access-Control-Allow-Credentials\": \"true\",\n \"Access-Control-Max-Age\": \"3600\",\n \"Access-Control-Allow-Methods\": \"GET, POST, PUT, DELETE, OPTIONS\",\n \"Access-Control-Allow-Headers\": CORS_ALLOWED_HEADERS,\n },\n onListening: () => {},\n setupMiddlewares: (middlewares: Array<MiddlewareEntry>, devServer: { app?: DevServerApp }) => {\n if (devServer.app) {\n return registerMiddlewares(devServer.app, middlewares, port, options);\n }\n return middlewares;\n },\n});\n\n/** Generates a dev server configuration for webpack. */\nexport const getIrisAppWebpackDevServer = async (\n webpackDevServerConfiguration: WebpackDevServerConfiguration = {},\n options: DevServerOptions = {}\n): Promise<WebpackDevServerConfiguration> => {\n const port = await getAvailablePort(22220, 22229);\n const baseConfig = await createIrisAppDevServerConfig(port, options);\n\n // Type assertion required: webpack-dev-server and rspack have nominally different but\n // structurally identical DevServer types. This is a known limitation acknowledged by\n // the rspack team. See: https://github.com/web-infra-dev/rspack/blob/main/packages/rspack-dev-server/src/server.ts\n // eslint-disable-next-line local-rules/no-typescript-assertion\n return { ...baseConfig, ...webpackDevServerConfiguration } as unknown as WebpackDevServerConfiguration;\n};\n\n/** Generates a dev server configuration for rspack. */\nexport const getIrisAppRspackDevServer = async (\n rspackDevServerConfiguration: RspackDevServer = {},\n options: DevServerOptions = {}\n): Promise<RspackDevServer> => {\n const port = await getAvailablePort(22220, 22229);\n const baseConfig = await createIrisAppDevServerConfig(port, options);\n\n // Type assertion required: webpack-dev-server and rspack have nominally different but\n // structurally identical DevServer types. This is a known limitation acknowledged by\n // the rspack team. See: https://github.com/web-infra-dev/rspack/blob/main/packages/rspack-dev-server/src/server.ts\n // eslint-disable-next-line local-rules/no-typescript-assertion\n return { ...baseConfig, ...rspackDevServerConfiguration } as unknown as RspackDevServer;\n};\n"]}
1
+ {"version":3,"file":"getIrisAppDevServer.js","sourceRoot":"","sources":["../../../../../libs/iris-app-sdk/iris-app-build-utilities/src/getIrisAppDevServer.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DevServer as RspackDevServer } from \"@rspack/core\";\nimport { createProxyMiddleware } from \"http-proxy-middleware\";\nimport type { Configuration as WebpackDevServerConfiguration } from \"webpack-dev-server\";\nimport { getAvailablePort } from \"./getAvailablePort\";\nimport type { ServerlessPortMap } from \"./spawnServerlessExtensions\";\n\nexport interface DevServerOptions {\n serverlessPortMap?: ServerlessPortMap;\n}\n\ntype MiddlewareRequest = {\n url?: string;\n originalUrl?: string;\n method?: string;\n headers: { origin?: string } & Record<string, string | undefined>;\n};\n\ntype MiddlewareResponse = {\n header: (name: string, value: string) => void;\n writeHead: (status: number, headers?: Record<string, string>) => void;\n end: (body?: string) => void;\n send: (body: unknown) => void;\n status: (code: number) => void;\n};\n\ntype MiddlewareNext = () => void;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- http-proxy-middleware types are complex\ntype ProxyHandler = (req: any, res: any, next: MiddlewareNext) => void;\n\ntype DevServerApp = { use: (path: string, handler: unknown) => void };\n\ntype MiddlewareEntry = { name?: string; path?: string; middleware: unknown };\n\n/** Parses the extension ID from an invoke URL path. Format: /invoke/@{org}/{app}/{extension-id}/{endpoint} */\nconst parseExtensionIdFromPath = (path: string): string | undefined => {\n const match = path.match(/^\\/invoke\\/@[^/]+\\/[^/]+\\/([^/]+)/);\n return match?.[1];\n};\n\n/** Extracts the endpoint path from an invoke URL. Format: /invoke/@{org}/{app}/{extension-id}/{endpoint} */\nconst extractEndpointPath = (path: string): string => {\n // Try with /invoke prefix first (for originalUrl), then without (for req.url after Express mount strips it)\n const matchWithInvoke = path.match(/^\\/invoke\\/@[^/]+\\/[^/]+\\/[^/]+(.*)$/);\n if (matchWithInvoke?.[1] !== undefined) {\n return matchWithInvoke[1] || \"/\";\n }\n const matchWithoutInvoke = path.match(/^\\/@[^/]+\\/[^/]+\\/[^/]+(.*)$/);\n return matchWithoutInvoke?.[1] || \"/\";\n};\n\nconst CORS_ALLOWED_HEADERS =\n \"X-Requested-With, baggage, content-type, Authorization, sentry-trace, session-id, commit-number, x-trackunitappversion\";\n\nconst DEFAULT_ORIGIN = \"https://dev.manager.trackunit.com\";\n\nconst getOrigin = (request: { headers: { origin?: string } }): string => request.headers.origin || DEFAULT_ORIGIN;\n\n/** Sets CORS headers on the response. */\nconst setCorsHeaders = (response: Pick<MiddlewareResponse, \"header\">, origin: string): void => {\n response.header(\"Access-Control-Allow-Origin\", origin);\n response.header(\"Access-Control-Allow-Headers\", CORS_ALLOWED_HEADERS);\n};\n\n/** Creates a proxy for the remote Iris platform (used when extension is not running locally). */\nconst createRemoteIrisProxy = (): ProxyHandler =>\n createProxyMiddleware({\n target: \"https://dev.iris.trackunit.app\",\n changeOrigin: true,\n secure: true,\n });\n\n/** Creates a proxy for a local serverless extension. */\nconst createLocalExtensionProxy = (target: string, extensionId: string): ProxyHandler =>\n createProxyMiddleware({\n target,\n changeOrigin: true,\n pathRewrite: path => extractEndpointPath(path),\n on: {\n error: (err, _proxyReq, proxyRes) => {\n // eslint-disable-next-line no-console\n console.error(`[Proxy] Error proxying to ${extensionId}:`, err.message);\n if (\"writeHead\" in proxyRes) {\n proxyRes.writeHead(502, { \"Content-Type\": \"application/json\" });\n proxyRes.end(JSON.stringify({ error: `Serverless extension ${extensionId} unavailable` }));\n }\n },\n },\n });\n\n/** Gets or creates a cached proxy for a local extension. */\nconst getOrCreateLocalProxy = (cache: Map<string, ProxyHandler>, extensionId: string, port: number): ProxyHandler => {\n const existing = cache.get(extensionId);\n if (existing !== undefined) {\n return existing;\n }\n\n const target = `http://localhost:${port}`;\n const newProxy = createLocalExtensionProxy(target, extensionId);\n cache.set(extensionId, newProxy);\n return newProxy;\n};\n\n/** Creates the /invoke proxy middleware that routes to local or remote extensions. */\nconst createInvokeProxyHandler = (\n serverlessPortMap: ServerlessPortMap | undefined,\n localProxyCache: Map<string, ProxyHandler>,\n remoteProxy: ProxyHandler\n) => {\n return (req: MiddlewareRequest, res: MiddlewareResponse, next: MiddlewareNext): void => {\n const fullPath = req.originalUrl ?? `/invoke${req.url ?? \"\"}`;\n const extensionId = parseExtensionIdFromPath(fullPath);\n const localPort = extensionId !== undefined ? serverlessPortMap?.get(extensionId) : undefined;\n\n if (localPort !== undefined && extensionId !== undefined) {\n const endpointPath = extractEndpointPath(fullPath);\n // eslint-disable-next-line no-console\n console.log(`[Proxy] ${fullPath} -> http://localhost:${localPort}${endpointPath}`);\n\n const proxy = getOrCreateLocalProxy(localProxyCache, extensionId, localPort);\n proxy(req, res, next);\n } else {\n remoteProxy(req, res, next);\n }\n };\n};\n\n/** CORS preflight middleware - responds to non-GET requests with allowed methods. */\nconst corsPreflightMiddleware = (\n req: Pick<MiddlewareRequest, \"method\">,\n res: Pick<MiddlewareResponse, \"end\">,\n next: MiddlewareNext\n): void => {\n if (req.method === \"GET\") {\n next();\n } else {\n res.end(\"GET, HEAD\");\n }\n};\n\n/** CORS origin header middleware - sets Access-Control-Allow-Origin on all requests. */\nconst createCorsOriginHandler = () => {\n return (\n request: Pick<MiddlewareRequest, \"headers\">,\n response: Pick<MiddlewareResponse, \"header\">,\n next: MiddlewareNext\n ): void => {\n response.header(\"Access-Control-Allow-Origin\", getOrigin(request));\n next();\n };\n};\n\n/** Creates the /manifestAndToken endpoint handler. */\nconst createManifestAndTokenHandler = (port: number) => {\n return async (\n request: Pick<MiddlewareRequest, \"headers\">,\n response: Pick<MiddlewareResponse, \"header\" | \"send\" | \"status\">\n ): Promise<void> => {\n setCorsHeaders(response, getOrigin(request));\n try {\n const resp = await fetch(`http://localhost:${port}/manifest.json`);\n const body = await resp.json();\n response.send({ manifest: body });\n } catch (e) {\n // eslint-disable-next-line no-console\n console.error(\"ERROR: \", e);\n response.status(500);\n response.send(e);\n }\n };\n};\n\n/** Creates the /extensionloader.js endpoint handler. */\nconst createExtensionLoaderHandler = () => {\n return async (\n request: Pick<MiddlewareRequest, \"headers\">,\n response: Pick<MiddlewareResponse, \"header\" | \"send\" | \"status\">\n ): Promise<void> => {\n setCorsHeaders(response, getOrigin(request));\n try {\n const url =\n process.env.LOCAL === \"true\"\n ? \"http://localhost:3000/extensionloader.js\"\n : \"https://iris.trackunit.app/extensionloader.js\";\n const resp = await fetch(url);\n const body = await resp.text();\n response.send(body);\n } catch (e) {\n // eslint-disable-next-line no-console\n console.error(\"ERROR: \", e);\n response.status(500);\n response.send(e);\n }\n };\n};\n\n/** Registers all middlewares on the dev server app. */\nconst registerMiddlewares = (\n app: DevServerApp,\n middlewares: Array<MiddlewareEntry>,\n port: number,\n options: DevServerOptions\n): Array<MiddlewareEntry> => {\n const localProxyCache = new Map<string, ProxyHandler>();\n const remoteProxy = createRemoteIrisProxy();\n\n // Register /invoke proxy\n app.use(\"/invoke\", createInvokeProxyHandler(options.serverlessPortMap, localProxyCache, remoteProxy));\n\n // Register CORS origin handler on all routes\n app.use(\"/\", createCorsOriginHandler());\n\n // Register endpoint handlers\n app.use(\"/manifestAndToken\", createManifestAndTokenHandler(port));\n app.use(\"/extensionloader.js\", createExtensionLoaderHandler());\n\n // Add CORS preflight to middleware array\n middlewares.push({\n name: \"cors-preflight\",\n path: \"/\",\n middleware: corsPreflightMiddleware,\n });\n\n return middlewares;\n};\n\n/** Creates the base Iris App dev server configuration. */\nconst createIrisAppDevServerConfig = async (port: number, options: DevServerOptions = {}) => ({\n port,\n historyApiFallback: true,\n headers: {\n \"Access-Control-Allow-Credentials\": \"true\",\n \"Access-Control-Max-Age\": \"3600\",\n \"Access-Control-Allow-Methods\": \"GET, POST, PUT, DELETE, OPTIONS\",\n \"Access-Control-Allow-Headers\": CORS_ALLOWED_HEADERS,\n },\n onListening: () => {},\n setupMiddlewares: (middlewares: Array<MiddlewareEntry>, devServer: { app?: DevServerApp }) => {\n if (devServer.app) {\n return registerMiddlewares(devServer.app, middlewares, port, options);\n }\n return middlewares;\n },\n});\n\n/** Generates a dev server configuration for webpack. */\nexport const getIrisAppWebpackDevServer = async (\n webpackDevServerConfiguration: WebpackDevServerConfiguration = {},\n options: DevServerOptions = {}\n): Promise<WebpackDevServerConfiguration> => {\n const port = await getAvailablePort(22220, 22229);\n const baseConfig = await createIrisAppDevServerConfig(port, options);\n\n // Type assertion required: webpack-dev-server and rspack have nominally different but\n // structurally identical DevServer types. This is a known limitation acknowledged by\n // the rspack team. See: https://github.com/web-infra-dev/rspack/blob/main/packages/rspack-dev-server/src/server.ts\n // eslint-disable-next-line local-rules/no-typescript-assertion\n return { ...baseConfig, ...webpackDevServerConfiguration } as unknown as WebpackDevServerConfiguration;\n};\n\n/** Generates a dev server configuration for rspack. */\nexport const getIrisAppRspackDevServer = async (\n rspackDevServerConfiguration: RspackDevServer = {},\n options: DevServerOptions = {}\n): Promise<RspackDevServer> => {\n const port = await getAvailablePort(22220, 22229);\n const baseConfig = await createIrisAppDevServerConfig(port, options);\n\n // Type assertion required: webpack-dev-server and rspack have nominally different but\n // structurally identical DevServer types. This is a known limitation acknowledged by\n // the rspack team. See: https://github.com/web-infra-dev/rspack/blob/main/packages/rspack-dev-server/src/server.ts\n // eslint-disable-next-line local-rules/no-typescript-assertion\n return { ...baseConfig, ...rspackDevServerConfiguration } as unknown as RspackDevServer;\n};\n"]}