@trackunit/iris-app-build-utilities 1.12.14 → 1.12.16
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 +14 -0
- package/package.json +4 -3
- package/src/getIrisAppDevServer.d.ts +8 -14
- package/src/getIrisAppDevServer.js +154 -86
- package/src/getIrisAppDevServer.js.map +1 -1
- package/src/index.d.ts +1 -0
- package/src/index.js +1 -0
- package/src/index.js.map +1 -1
- package/src/spawnServerlessExtensions.d.ts +16 -0
- package/src/spawnServerlessExtensions.js +103 -0
- package/src/spawnServerlessExtensions.js.map +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## 1.12.16 (2026-01-30)
|
|
2
|
+
|
|
3
|
+
### 🧱 Updated Dependencies
|
|
4
|
+
|
|
5
|
+
- Updated iris-app-api to 1.14.15
|
|
6
|
+
- Updated shared-utils to 1.13.16
|
|
7
|
+
|
|
8
|
+
## 1.12.15 (2026-01-30)
|
|
9
|
+
|
|
10
|
+
### 🧱 Updated Dependencies
|
|
11
|
+
|
|
12
|
+
- Updated iris-app-api to 1.14.14
|
|
13
|
+
- Updated shared-utils to 1.13.15
|
|
14
|
+
|
|
1
15
|
## 1.12.14 (2026-01-29)
|
|
2
16
|
|
|
3
17
|
### 🧱 Updated Dependencies
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/iris-app-build-utilities",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.16",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
5
5
|
"repository": "https://github.com/Trackunit/manager",
|
|
6
6
|
"engines": {
|
|
@@ -17,9 +17,10 @@
|
|
|
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.
|
|
20
|
+
"@trackunit/iris-app-api": "1.14.15",
|
|
21
21
|
"@nx/devkit": "22.0.4",
|
|
22
|
-
"@trackunit/shared-utils": "1.13.
|
|
22
|
+
"@trackunit/shared-utils": "1.13.16",
|
|
23
|
+
"http-proxy-middleware": "3.0.5",
|
|
23
24
|
"pacote": "^21.0.4",
|
|
24
25
|
"semver": "7.5.4"
|
|
25
26
|
},
|
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import type { DevServer as RspackDevServer } from "@rspack/core";
|
|
2
2
|
import type { Configuration as WebpackDevServerConfiguration } from "webpack-dev-server";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* Generates a dev server configuration for rspack configuration.
|
|
12
|
-
*
|
|
13
|
-
* @param rspackDevServerConfiguration The configuration to extend.
|
|
14
|
-
* @returns {RspackDevServer} The dev server for rspack.
|
|
15
|
-
*/
|
|
16
|
-
export declare const getIrisAppRspackDevServer: (rspackDevServerConfiguration?: RspackDevServer) => Promise<RspackDevServer>;
|
|
3
|
+
import type { ServerlessPortMap } from "./spawnServerlessExtensions";
|
|
4
|
+
export interface DevServerOptions {
|
|
5
|
+
serverlessPortMap?: ServerlessPortMap;
|
|
6
|
+
}
|
|
7
|
+
/** Generates a dev server configuration for webpack. */
|
|
8
|
+
export declare const getIrisAppWebpackDevServer: (webpackDevServerConfiguration?: WebpackDevServerConfiguration, options?: DevServerOptions) => Promise<WebpackDevServerConfiguration>;
|
|
9
|
+
/** Generates a dev server configuration for rspack. */
|
|
10
|
+
export declare const getIrisAppRspackDevServer: (rspackDevServerConfiguration?: RspackDevServer, options?: DevServerOptions) => Promise<RspackDevServer>;
|
|
@@ -1,98 +1,171 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getIrisAppRspackDevServer = exports.getIrisAppWebpackDevServer = void 0;
|
|
4
|
+
const http_proxy_middleware_1 = require("http-proxy-middleware");
|
|
4
5
|
const getAvailablePort_1 = require("./getAvailablePort");
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
/** Parses the extension ID from an invoke URL path. Format: /invoke/@{org}/{app}/{extension-id}/{endpoint} */
|
|
7
|
+
const parseExtensionIdFromPath = (path) => {
|
|
8
|
+
const match = path.match(/^\/invoke\/@[^/]+\/[^/]+\/([^/]+)/);
|
|
9
|
+
return match?.[1];
|
|
10
|
+
};
|
|
11
|
+
/** Extracts the endpoint path from an invoke URL. Format: /invoke/@{org}/{app}/{extension-id}/{endpoint} */
|
|
12
|
+
const extractEndpointPath = (path) => {
|
|
13
|
+
const match = path.match(/^\/invoke\/@[^/]+\/[^/]+\/[^/]+(.*)$/);
|
|
14
|
+
return match?.[1] || "/";
|
|
15
|
+
};
|
|
16
|
+
const CORS_ALLOWED_HEADERS = "X-Requested-With, baggage, content-type, Authorization, sentry-trace, session-id, commit-number, x-trackunitappversion";
|
|
17
|
+
const DEFAULT_ORIGIN = "https://dev.manager.trackunit.com";
|
|
18
|
+
const getOrigin = (request) => request.headers.origin || DEFAULT_ORIGIN;
|
|
19
|
+
/** Sets CORS headers on the response. */
|
|
20
|
+
const setCorsHeaders = (response, origin) => {
|
|
21
|
+
response.header("Access-Control-Allow-Origin", origin);
|
|
22
|
+
response.header("Access-Control-Allow-Headers", CORS_ALLOWED_HEADERS);
|
|
23
|
+
};
|
|
24
|
+
/** Creates a proxy for the remote Iris platform (used when extension is not running locally). */
|
|
25
|
+
const createRemoteIrisProxy = () => (0, http_proxy_middleware_1.createProxyMiddleware)({
|
|
26
|
+
target: "https://dev.iris.trackunit.app",
|
|
27
|
+
changeOrigin: true,
|
|
28
|
+
secure: true,
|
|
29
|
+
});
|
|
30
|
+
/** Creates a proxy for a local serverless extension. */
|
|
31
|
+
const createLocalExtensionProxy = (target, extensionId) => (0, http_proxy_middleware_1.createProxyMiddleware)({
|
|
32
|
+
target,
|
|
33
|
+
changeOrigin: true,
|
|
34
|
+
pathRewrite: path => extractEndpointPath(path),
|
|
35
|
+
on: {
|
|
36
|
+
error: (err, _proxyReq, proxyRes) => {
|
|
37
|
+
// eslint-disable-next-line no-console
|
|
38
|
+
console.error(`[Proxy] Error proxying to ${extensionId}:`, err.message);
|
|
39
|
+
if ("writeHead" in proxyRes) {
|
|
40
|
+
proxyRes.writeHead(502, { "Content-Type": "application/json" });
|
|
41
|
+
proxyRes.end(JSON.stringify({ error: `Serverless extension ${extensionId} unavailable` }));
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
/** Gets or creates a cached proxy for a local extension. */
|
|
47
|
+
const getOrCreateLocalProxy = (cache, extensionId, port) => {
|
|
48
|
+
const existing = cache.get(extensionId);
|
|
49
|
+
if (existing !== undefined) {
|
|
50
|
+
return existing;
|
|
51
|
+
}
|
|
52
|
+
const target = `http://localhost:${port}`;
|
|
53
|
+
const newProxy = createLocalExtensionProxy(target, extensionId);
|
|
54
|
+
cache.set(extensionId, newProxy);
|
|
55
|
+
return newProxy;
|
|
56
|
+
};
|
|
57
|
+
/** Creates the /invoke proxy middleware that routes to local or remote extensions. */
|
|
58
|
+
const createInvokeProxyHandler = (serverlessPortMap, localProxyCache, remoteProxy) => {
|
|
59
|
+
return (req, res, next) => {
|
|
60
|
+
const fullPath = req.originalUrl ?? `/invoke${req.url ?? ""}`;
|
|
61
|
+
const extensionId = parseExtensionIdFromPath(fullPath);
|
|
62
|
+
const localPort = extensionId !== undefined ? serverlessPortMap?.get(extensionId) : undefined;
|
|
63
|
+
if (localPort !== undefined && extensionId !== undefined) {
|
|
64
|
+
const endpointPath = extractEndpointPath(fullPath);
|
|
65
|
+
// eslint-disable-next-line no-console
|
|
66
|
+
console.log(`[Proxy] ${fullPath} -> http://localhost:${localPort}${endpointPath}`);
|
|
67
|
+
const proxy = getOrCreateLocalProxy(localProxyCache, extensionId, localPort);
|
|
68
|
+
proxy(req, res, next);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
remoteProxy(req, res, next);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
/** CORS preflight middleware - responds to non-GET requests with allowed methods. */
|
|
76
|
+
const corsPreflightMiddleware = (req, res, next) => {
|
|
77
|
+
if (req.method === "GET") {
|
|
78
|
+
next();
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
res.end("GET, HEAD");
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
/** CORS origin header middleware - sets Access-Control-Allow-Origin on all requests. */
|
|
85
|
+
const createCorsOriginHandler = () => {
|
|
86
|
+
return (request, response, next) => {
|
|
87
|
+
response.header("Access-Control-Allow-Origin", getOrigin(request));
|
|
88
|
+
next();
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
/** Creates the /manifestAndToken endpoint handler. */
|
|
92
|
+
const createManifestAndTokenHandler = (port) => {
|
|
93
|
+
return async (request, response) => {
|
|
94
|
+
setCorsHeaders(response, getOrigin(request));
|
|
95
|
+
try {
|
|
96
|
+
const resp = await fetch(`http://localhost:${port}/manifest.json`);
|
|
97
|
+
const body = await resp.json();
|
|
98
|
+
response.send({ manifest: body });
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
// eslint-disable-next-line no-console
|
|
102
|
+
console.error("ERROR: ", e);
|
|
103
|
+
response.status(500);
|
|
104
|
+
response.send(e);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
/** Creates the /extensionloader.js endpoint handler. */
|
|
109
|
+
const createExtensionLoaderHandler = () => {
|
|
110
|
+
return async (request, response) => {
|
|
111
|
+
setCorsHeaders(response, getOrigin(request));
|
|
112
|
+
try {
|
|
113
|
+
const url = process.env.LOCAL === "true"
|
|
114
|
+
? "http://localhost:3000/extensionloader.js"
|
|
115
|
+
: "https://iris.trackunit.app/extensionloader.js";
|
|
116
|
+
const resp = await fetch(url);
|
|
117
|
+
const body = await resp.text();
|
|
118
|
+
response.send(body);
|
|
119
|
+
}
|
|
120
|
+
catch (e) {
|
|
121
|
+
// eslint-disable-next-line no-console
|
|
122
|
+
console.error("ERROR: ", e);
|
|
123
|
+
response.status(500);
|
|
124
|
+
response.send(e);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
/** Registers all middlewares on the dev server app. */
|
|
129
|
+
const registerMiddlewares = (app, middlewares, port, options) => {
|
|
130
|
+
const localProxyCache = new Map();
|
|
131
|
+
const remoteProxy = createRemoteIrisProxy();
|
|
132
|
+
// Register /invoke proxy
|
|
133
|
+
app.use("/invoke", createInvokeProxyHandler(options.serverlessPortMap, localProxyCache, remoteProxy));
|
|
134
|
+
// Register CORS origin handler on all routes
|
|
135
|
+
app.use("/", createCorsOriginHandler());
|
|
136
|
+
// Register endpoint handlers
|
|
137
|
+
app.use("/manifestAndToken", createManifestAndTokenHandler(port));
|
|
138
|
+
app.use("/extensionloader.js", createExtensionLoaderHandler());
|
|
139
|
+
// Add CORS preflight to middleware array
|
|
140
|
+
middlewares.push({
|
|
141
|
+
name: "cors-preflight",
|
|
142
|
+
path: "/",
|
|
143
|
+
middleware: corsPreflightMiddleware,
|
|
144
|
+
});
|
|
145
|
+
return middlewares;
|
|
146
|
+
};
|
|
147
|
+
/** Creates the base Iris App dev server configuration. */
|
|
148
|
+
const createIrisAppDevServerConfig = async (port, options = {}) => ({
|
|
15
149
|
port,
|
|
16
150
|
historyApiFallback: true,
|
|
17
151
|
headers: {
|
|
18
152
|
"Access-Control-Allow-Credentials": "true",
|
|
19
153
|
"Access-Control-Max-Age": "3600",
|
|
20
154
|
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
|
21
|
-
"Access-Control-Allow-Headers":
|
|
155
|
+
"Access-Control-Allow-Headers": CORS_ALLOWED_HEADERS,
|
|
22
156
|
},
|
|
23
|
-
proxy: [
|
|
24
|
-
{
|
|
25
|
-
context: ["/invoke"],
|
|
26
|
-
target: "https://dev.iris.trackunit.app",
|
|
27
|
-
changeOrigin: true,
|
|
28
|
-
secure: true,
|
|
29
|
-
logLevel: "debug",
|
|
30
|
-
},
|
|
31
|
-
],
|
|
32
157
|
onListening: () => { },
|
|
33
158
|
setupMiddlewares: (middlewares, devServer) => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
middleware: (req, res, next) => {
|
|
38
|
-
if (req.method === "GET") {
|
|
39
|
-
next();
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
res.end?.("GET, HEAD");
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
devServer.app?.use("/", (request, response, next) => {
|
|
47
|
-
response.header("Access-Control-Allow-Origin", request.headers.origin || "https://dev.manager.trackunit.com");
|
|
48
|
-
next();
|
|
49
|
-
});
|
|
50
|
-
devServer.app?.use("/manifestAndToken", async (request, response) => {
|
|
51
|
-
response.header("Access-Control-Allow-Origin", request.headers.origin || "https://dev.manager.trackunit.com");
|
|
52
|
-
response.header("Access-Control-Allow-Headers", "X-Requested-With, baggage, content-type, Authorization, sentry-trace, session-id, commit-number, x-trackunitappversion");
|
|
53
|
-
try {
|
|
54
|
-
const resp = await fetch(`http://localhost:${port}/manifest.json`);
|
|
55
|
-
const body = await resp.json();
|
|
56
|
-
response.send({ manifest: body });
|
|
57
|
-
}
|
|
58
|
-
catch (e) {
|
|
59
|
-
// eslint-disable-next-line no-console
|
|
60
|
-
console.error("ERROR: ", e);
|
|
61
|
-
response.status(500);
|
|
62
|
-
response.send(e);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
devServer.app?.use("/extensionloader.js", async (request, response) => {
|
|
66
|
-
response.header("Access-Control-Allow-Origin", request.headers.origin || "https://dev.manager.trackunit.com");
|
|
67
|
-
response.header("Access-Control-Allow-Headers", "X-Requested-With, baggage, content-type, Authorization, sentry-trace, session-id, commit-number, x-trackunitappversion");
|
|
68
|
-
try {
|
|
69
|
-
let url = "https://iris.trackunit.app/extensionloader.js";
|
|
70
|
-
if (process.env.LOCAL === "true") {
|
|
71
|
-
url = "http://localhost:3000/extensionloader.js";
|
|
72
|
-
}
|
|
73
|
-
const resp = await fetch(url);
|
|
74
|
-
const body = await resp.text();
|
|
75
|
-
response.send(body);
|
|
76
|
-
}
|
|
77
|
-
catch (e) {
|
|
78
|
-
// eslint-disable-next-line no-console
|
|
79
|
-
console.error("ERROR: ", e);
|
|
80
|
-
response.status(500);
|
|
81
|
-
response.send(e);
|
|
82
|
-
}
|
|
83
|
-
});
|
|
159
|
+
if (devServer.app) {
|
|
160
|
+
return registerMiddlewares(devServer.app, middlewares, port, options);
|
|
161
|
+
}
|
|
84
162
|
return middlewares;
|
|
85
163
|
},
|
|
86
164
|
});
|
|
87
|
-
/**
|
|
88
|
-
|
|
89
|
-
*
|
|
90
|
-
* @param webpackDevServerConfiguration The configuration to extend.
|
|
91
|
-
* @returns {WebpackDevServerConfiguration} The dev server for webpack.
|
|
92
|
-
*/
|
|
93
|
-
const getIrisAppWebpackDevServer = async (webpackDevServerConfiguration = {}) => {
|
|
165
|
+
/** Generates a dev server configuration for webpack. */
|
|
166
|
+
const getIrisAppWebpackDevServer = async (webpackDevServerConfiguration = {}, options = {}) => {
|
|
94
167
|
const port = await (0, getAvailablePort_1.getAvailablePort)(22220, 22229);
|
|
95
|
-
const baseConfig = await createIrisAppDevServerConfig(port);
|
|
168
|
+
const baseConfig = await createIrisAppDevServerConfig(port, options);
|
|
96
169
|
// Type assertion required: webpack-dev-server and rspack have nominally different but
|
|
97
170
|
// structurally identical DevServer types. This is a known limitation acknowledged by
|
|
98
171
|
// the rspack team. See: https://github.com/web-infra-dev/rspack/blob/main/packages/rspack-dev-server/src/server.ts
|
|
@@ -100,15 +173,10 @@ const getIrisAppWebpackDevServer = async (webpackDevServerConfiguration = {}) =>
|
|
|
100
173
|
return { ...baseConfig, ...webpackDevServerConfiguration };
|
|
101
174
|
};
|
|
102
175
|
exports.getIrisAppWebpackDevServer = getIrisAppWebpackDevServer;
|
|
103
|
-
/**
|
|
104
|
-
|
|
105
|
-
*
|
|
106
|
-
* @param rspackDevServerConfiguration The configuration to extend.
|
|
107
|
-
* @returns {RspackDevServer} The dev server for rspack.
|
|
108
|
-
*/
|
|
109
|
-
const getIrisAppRspackDevServer = async (rspackDevServerConfiguration = {}) => {
|
|
176
|
+
/** Generates a dev server configuration for rspack. */
|
|
177
|
+
const getIrisAppRspackDevServer = async (rspackDevServerConfiguration = {}, options = {}) => {
|
|
110
178
|
const port = await (0, getAvailablePort_1.getAvailablePort)(22220, 22229);
|
|
111
|
-
const baseConfig = await createIrisAppDevServerConfig(port);
|
|
179
|
+
const baseConfig = await createIrisAppDevServerConfig(port, options);
|
|
112
180
|
// Type assertion required: webpack-dev-server and rspack have nominally different but
|
|
113
181
|
// structurally identical DevServer types. This is a known limitation acknowledged by
|
|
114
182
|
// the rspack team. See: https://github.com/web-infra-dev/rspack/blob/main/packages/rspack-dev-server/src/server.ts
|
|
@@ -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 type { Configuration as WebpackDevServerConfiguration } from \"webpack-dev-server\";\nimport { getAvailablePort } from \"./getAvailablePort\";\n\n/**\n * Creates the base Iris App dev server configuration.\n *\n * Returns the config without explicit typing because webpack-dev-server's Configuration\n * and rspack's DevServer are nominally different types despite being structurally identical.\n * The rspack team acknowledges this type incompatibility in their own codebase with @ts-ignore.\n *\n * @see https://github.com/web-infra-dev/rspack/blob/main/packages/rspack-dev-server/src/server.ts\n */\nconst createIrisAppDevServerConfig = async (port: number) => ({\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\":\n \"X-Requested-With, baggage, content-type, Authorization, sentry-trace, session-id, commit-number, x-trackunitappversion\",\n },\n proxy: [\n {\n context: [\"/invoke\"],\n target: \"https://dev.iris.trackunit.app\",\n changeOrigin: true,\n secure: true,\n logLevel: \"debug\",\n },\n ],\n onListening: () => {},\n setupMiddlewares: (\n middlewares: Array<{ name?: string; path?: string; middleware: unknown }>,\n devServer: { app?: { use: (path: string, handler: unknown) => void } }\n ) => {\n middlewares.push({\n name: \"cors-preflight\",\n path: \"/\",\n middleware: (\n req: { method?: string },\n res: { send?: (body: unknown) => void; end?: (body: string) => void },\n next: () => void\n ) => {\n if (req.method === \"GET\") {\n next();\n } else {\n res.end?.(\"GET, HEAD\");\n }\n },\n });\n devServer.app?.use(\n \"/\",\n (\n request: { headers: { origin?: string } },\n response: { header: (name: string, value: string) => void },\n next: () => void\n ) => {\n response.header(\"Access-Control-Allow-Origin\", request.headers.origin || \"https://dev.manager.trackunit.com\");\n next();\n }\n );\n devServer.app?.use(\n \"/manifestAndToken\",\n async (\n request: { headers: { origin?: string } },\n response: {\n header: (name: string, value: string) => void;\n send: (body: unknown) => void;\n status: (code: number) => void;\n }\n ) => {\n response.header(\"Access-Control-Allow-Origin\", request.headers.origin || \"https://dev.manager.trackunit.com\");\n response.header(\n \"Access-Control-Allow-Headers\",\n \"X-Requested-With, baggage, content-type, Authorization, sentry-trace, session-id, commit-number, x-trackunitappversion\"\n );\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 devServer.app?.use(\n \"/extensionloader.js\",\n async (\n request: { headers: { origin?: string } },\n response: {\n header: (name: string, value: string) => void;\n send: (body: unknown) => void;\n status: (code: number) => void;\n }\n ) => {\n response.header(\"Access-Control-Allow-Origin\", request.headers.origin || \"https://dev.manager.trackunit.com\");\n response.header(\n \"Access-Control-Allow-Headers\",\n \"X-Requested-With, baggage, content-type, Authorization, sentry-trace, session-id, commit-number, x-trackunitappversion\"\n );\n try {\n let url = \"https://iris.trackunit.app/extensionloader.js\";\n if (process.env.LOCAL === \"true\") {\n url = \"http://localhost:3000/extensionloader.js\";\n }\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 return middlewares;\n },\n});\n\n/**\n * Generates a dev server configuration for webpack configuration.\n *\n * @param webpackDevServerConfiguration The configuration to extend.\n * @returns {WebpackDevServerConfiguration} The dev server for webpack.\n */\nexport const getIrisAppWebpackDevServer = async (\n webpackDevServerConfiguration: WebpackDevServerConfiguration = {}\n): Promise<WebpackDevServerConfiguration> => {\n const port = await getAvailablePort(22220, 22229);\n const baseConfig = await createIrisAppDevServerConfig(port);\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/**\n * Generates a dev server configuration for rspack configuration.\n *\n * @param rspackDevServerConfiguration The configuration to extend.\n * @returns {RspackDevServer} The dev server for rspack.\n */\nexport const getIrisAppRspackDevServer = async (\n rspackDevServerConfiguration: RspackDevServer = {}\n): Promise<RspackDevServer> => {\n const port = await getAvailablePort(22220, 22229);\n const baseConfig = await createIrisAppDevServerConfig(port);\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 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"]}
|
package/src/index.d.ts
CHANGED
package/src/index.js
CHANGED
|
@@ -15,4 +15,5 @@ tslib_1.__exportStar(require("./plugin-shared/consoleUtils"), exports);
|
|
|
15
15
|
tslib_1.__exportStar(require("./plugin-shared/customFieldUtil"), exports);
|
|
16
16
|
tslib_1.__exportStar(require("./plugin-shared/extensionUtil"), exports);
|
|
17
17
|
tslib_1.__exportStar(require("./plugin-shared/indexHtmlUtil"), exports);
|
|
18
|
+
tslib_1.__exportStar(require("./spawnServerlessExtensions"), exports);
|
|
18
19
|
//# sourceMappingURL=index.js.map
|
package/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../libs/iris-app-sdk/iris-app-build-utilities/src/index.ts"],"names":[],"mappings":";;;AAAA,gEAAsC;AACtC,+DAAqC;AACrC,mEAAyC;AACzC,6DAAmC;AACnC,4DAAkC;AAClC,iEAAuC;AACvC,oEAA0C;AAC1C,gEAAsC;AACtC,kEAAwC;AACxC,qEAA2C;AAC3C,uEAA6C;AAC7C,0EAAgD;AAChD,wEAA8C;AAC9C,wEAA8C","sourcesContent":["export * from \"./checkPackageVersion\";\nexport * from \"./enableTsConfigPath\";\nexport * from \"./getAliasesFromTsConfig\";\nexport * from \"./getAvailablePort\";\nexport * from \"./getCopyPatterns\";\nexport * from \"./getExposedExtensions\";\nexport * from \"./getGraphqlCodeGenConfig\";\nexport * from \"./getIrisAppDevServer\";\nexport * from \"./getSharedDependencies\";\nexport * from \"./getTailwindContentForApp\";\nexport * from \"./plugin-shared/consoleUtils\";\nexport * from \"./plugin-shared/customFieldUtil\";\nexport * from \"./plugin-shared/extensionUtil\";\nexport * from \"./plugin-shared/indexHtmlUtil\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../libs/iris-app-sdk/iris-app-build-utilities/src/index.ts"],"names":[],"mappings":";;;AAAA,gEAAsC;AACtC,+DAAqC;AACrC,mEAAyC;AACzC,6DAAmC;AACnC,4DAAkC;AAClC,iEAAuC;AACvC,oEAA0C;AAC1C,gEAAsC;AACtC,kEAAwC;AACxC,qEAA2C;AAC3C,uEAA6C;AAC7C,0EAAgD;AAChD,wEAA8C;AAC9C,wEAA8C;AAC9C,sEAA4C","sourcesContent":["export * from \"./checkPackageVersion\";\nexport * from \"./enableTsConfigPath\";\nexport * from \"./getAliasesFromTsConfig\";\nexport * from \"./getAvailablePort\";\nexport * from \"./getCopyPatterns\";\nexport * from \"./getExposedExtensions\";\nexport * from \"./getGraphqlCodeGenConfig\";\nexport * from \"./getIrisAppDevServer\";\nexport * from \"./getSharedDependencies\";\nexport * from \"./getTailwindContentForApp\";\nexport * from \"./plugin-shared/consoleUtils\";\nexport * from \"./plugin-shared/customFieldUtil\";\nexport * from \"./plugin-shared/extensionUtil\";\nexport * from \"./plugin-shared/indexHtmlUtil\";\nexport * from \"./spawnServerlessExtensions\";\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { IrisAppManifest } from "@trackunit/iris-app-api";
|
|
2
|
+
import { ChildProcess } from "child_process";
|
|
3
|
+
export type ServerlessPortMap = Map<string, number>;
|
|
4
|
+
export interface SpawnedServerlessExtensions {
|
|
5
|
+
portMap: ServerlessPortMap;
|
|
6
|
+
processes: Array<ChildProcess>;
|
|
7
|
+
cleanup: () => void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Spawns serve processes for all serverless function extensions in the manifest.
|
|
11
|
+
*
|
|
12
|
+
* @param manifest The Iris App manifest containing extensions
|
|
13
|
+
* @param workspaceRoot The root directory of the NX workspace
|
|
14
|
+
* @returns {Promise<SpawnedServerlessExtensions>} Object containing port map, spawned processes, and cleanup function
|
|
15
|
+
*/
|
|
16
|
+
export declare const spawnServerlessExtensions: (manifest: IrisAppManifest, workspaceRoot: string) => Promise<SpawnedServerlessExtensions>;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.spawnServerlessExtensions = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const getAvailablePort_1 = require("./getAvailablePort");
|
|
6
|
+
const SERVERLESS_PORT_MIN = 22230;
|
|
7
|
+
const SERVERLESS_PORT_MAX = 22239;
|
|
8
|
+
/**
|
|
9
|
+
* Spawns serve processes for all serverless function extensions in the manifest.
|
|
10
|
+
*
|
|
11
|
+
* @param manifest The Iris App manifest containing extensions
|
|
12
|
+
* @param workspaceRoot The root directory of the NX workspace
|
|
13
|
+
* @returns {Promise<SpawnedServerlessExtensions>} Object containing port map, spawned processes, and cleanup function
|
|
14
|
+
*/
|
|
15
|
+
const spawnServerlessExtensions = async (manifest, workspaceRoot) => {
|
|
16
|
+
const portMap = new Map();
|
|
17
|
+
const processes = [];
|
|
18
|
+
const serverlessExtensions = manifest.extensions.filter(ext => ext.type === "SERVERLESS_FUNCTION_EXTENSION");
|
|
19
|
+
if (serverlessExtensions.length === 0) {
|
|
20
|
+
return {
|
|
21
|
+
portMap,
|
|
22
|
+
processes,
|
|
23
|
+
cleanup: () => { },
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
// eslint-disable-next-line no-console
|
|
27
|
+
console.log(`\n[Serverless] Found ${serverlessExtensions.length} serverless extension(s)`);
|
|
28
|
+
let nextPort = SERVERLESS_PORT_MIN;
|
|
29
|
+
for (const extension of serverlessExtensions) {
|
|
30
|
+
const extensionId = extension.id;
|
|
31
|
+
// Find available port
|
|
32
|
+
let port;
|
|
33
|
+
try {
|
|
34
|
+
port = await (0, getAvailablePort_1.getAvailablePort)(nextPort, SERVERLESS_PORT_MAX);
|
|
35
|
+
nextPort = port + 1;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// eslint-disable-next-line no-console
|
|
39
|
+
console.error(`[Serverless] No available ports for ${extensionId}, skipping`);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
// eslint-disable-next-line no-console
|
|
43
|
+
console.log(`[Serverless] Starting ${extensionId} on port ${port}`);
|
|
44
|
+
const child = (0, child_process_1.spawn)("npx", ["nx", "run", `${extensionId}:serve`], {
|
|
45
|
+
cwd: workspaceRoot,
|
|
46
|
+
env: {
|
|
47
|
+
...process.env,
|
|
48
|
+
PORT: String(port),
|
|
49
|
+
},
|
|
50
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
51
|
+
shell: true,
|
|
52
|
+
});
|
|
53
|
+
// Prefix output with extension name
|
|
54
|
+
child.stdout.on("data", (data) => {
|
|
55
|
+
const lines = data.toString().trim().split("\n");
|
|
56
|
+
for (const line of lines) {
|
|
57
|
+
// eslint-disable-next-line no-console
|
|
58
|
+
console.log(`[${extensionId}] ${line}`);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
child.stderr.on("data", (data) => {
|
|
62
|
+
const lines = data.toString().trim().split("\n");
|
|
63
|
+
for (const line of lines) {
|
|
64
|
+
// eslint-disable-next-line no-console
|
|
65
|
+
console.error(`[${extensionId}] ${line}`);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
child.on("error", err => {
|
|
69
|
+
// eslint-disable-next-line no-console
|
|
70
|
+
console.error(`[Serverless] Failed to start ${extensionId}:`, err.message);
|
|
71
|
+
});
|
|
72
|
+
child.on("exit", (code, signal) => {
|
|
73
|
+
if (code !== null && code !== 0) {
|
|
74
|
+
// eslint-disable-next-line no-console
|
|
75
|
+
console.error(`[Serverless] ${extensionId} exited with code ${code}`);
|
|
76
|
+
}
|
|
77
|
+
else if (signal) {
|
|
78
|
+
// eslint-disable-next-line no-console
|
|
79
|
+
console.log(`[Serverless] ${extensionId} terminated by signal ${signal}`);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
portMap.set(extensionId, port);
|
|
83
|
+
processes.push(child);
|
|
84
|
+
}
|
|
85
|
+
const cleanup = () => {
|
|
86
|
+
// Remove process event handlers to prevent listener accumulation
|
|
87
|
+
process.off("exit", cleanup);
|
|
88
|
+
process.off("SIGINT", cleanup);
|
|
89
|
+
process.off("SIGTERM", cleanup);
|
|
90
|
+
for (const child of processes) {
|
|
91
|
+
if (!child.killed) {
|
|
92
|
+
child.kill("SIGTERM");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
// Handle parent process exit
|
|
97
|
+
process.on("exit", cleanup);
|
|
98
|
+
process.on("SIGINT", cleanup);
|
|
99
|
+
process.on("SIGTERM", cleanup);
|
|
100
|
+
return { portMap, processes, cleanup };
|
|
101
|
+
};
|
|
102
|
+
exports.spawnServerlessExtensions = spawnServerlessExtensions;
|
|
103
|
+
//# sourceMappingURL=spawnServerlessExtensions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawnServerlessExtensions.js","sourceRoot":"","sources":["../../../../../libs/iris-app-sdk/iris-app-build-utilities/src/spawnServerlessExtensions.ts"],"names":[],"mappings":"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","sourcesContent":["import { IrisAppManifest } from \"@trackunit/iris-app-api\";\nimport { ChildProcess, spawn } from \"child_process\";\nimport { getAvailablePort } from \"./getAvailablePort\";\n\nconst SERVERLESS_PORT_MIN = 22230;\nconst SERVERLESS_PORT_MAX = 22239;\n\nexport type ServerlessPortMap = Map<string, number>;\n\nexport interface SpawnedServerlessExtensions {\n portMap: ServerlessPortMap;\n processes: Array<ChildProcess>;\n cleanup: () => void;\n}\n\n/**\n * Spawns serve processes for all serverless function extensions in the manifest.\n *\n * @param manifest The Iris App manifest containing extensions\n * @param workspaceRoot The root directory of the NX workspace\n * @returns {Promise<SpawnedServerlessExtensions>} Object containing port map, spawned processes, and cleanup function\n */\nexport const spawnServerlessExtensions = async (\n manifest: IrisAppManifest,\n workspaceRoot: string\n): Promise<SpawnedServerlessExtensions> => {\n const portMap: ServerlessPortMap = new Map();\n const processes: Array<ChildProcess> = [];\n\n const serverlessExtensions = manifest.extensions.filter(ext => ext.type === \"SERVERLESS_FUNCTION_EXTENSION\");\n\n if (serverlessExtensions.length === 0) {\n return {\n portMap,\n processes,\n cleanup: () => {},\n };\n }\n\n // eslint-disable-next-line no-console\n console.log(`\\n[Serverless] Found ${serverlessExtensions.length} serverless extension(s)`);\n\n let nextPort = SERVERLESS_PORT_MIN;\n\n for (const extension of serverlessExtensions) {\n const extensionId = extension.id;\n\n // Find available port\n let port: number;\n try {\n port = await getAvailablePort(nextPort, SERVERLESS_PORT_MAX);\n nextPort = port + 1;\n } catch {\n // eslint-disable-next-line no-console\n console.error(`[Serverless] No available ports for ${extensionId}, skipping`);\n continue;\n }\n\n // eslint-disable-next-line no-console\n console.log(`[Serverless] Starting ${extensionId} on port ${port}`);\n\n const child = spawn(\"npx\", [\"nx\", \"run\", `${extensionId}:serve`], {\n cwd: workspaceRoot,\n env: {\n ...process.env,\n PORT: String(port),\n },\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n shell: true,\n });\n\n // Prefix output with extension name\n child.stdout.on(\"data\", (data: Buffer) => {\n const lines = data.toString().trim().split(\"\\n\");\n for (const line of lines) {\n // eslint-disable-next-line no-console\n console.log(`[${extensionId}] ${line}`);\n }\n });\n\n child.stderr.on(\"data\", (data: Buffer) => {\n const lines = data.toString().trim().split(\"\\n\");\n for (const line of lines) {\n // eslint-disable-next-line no-console\n console.error(`[${extensionId}] ${line}`);\n }\n });\n\n child.on(\"error\", err => {\n // eslint-disable-next-line no-console\n console.error(`[Serverless] Failed to start ${extensionId}:`, err.message);\n });\n\n child.on(\"exit\", (code, signal) => {\n if (code !== null && code !== 0) {\n // eslint-disable-next-line no-console\n console.error(`[Serverless] ${extensionId} exited with code ${code}`);\n } else if (signal) {\n // eslint-disable-next-line no-console\n console.log(`[Serverless] ${extensionId} terminated by signal ${signal}`);\n }\n });\n\n portMap.set(extensionId, port);\n processes.push(child);\n }\n\n const cleanup = () => {\n // Remove process event handlers to prevent listener accumulation\n process.off(\"exit\", cleanup);\n process.off(\"SIGINT\", cleanup);\n process.off(\"SIGTERM\", cleanup);\n\n for (const child of processes) {\n if (!child.killed) {\n child.kill(\"SIGTERM\");\n }\n }\n };\n\n // Handle parent process exit\n process.on(\"exit\", cleanup);\n process.on(\"SIGINT\", cleanup);\n process.on(\"SIGTERM\", cleanup);\n\n return { portMap, processes, cleanup };\n};\n"]}
|