@lensmcp/cluster 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +130 -0
  3. package/basic-ssl.d.ts +7 -0
  4. package/basic-ssl.d.ts.map +1 -0
  5. package/basic-ssl.js +158 -0
  6. package/build-scope-patterns.d.ts +12 -0
  7. package/build-scope-patterns.d.ts.map +1 -0
  8. package/build-scope-patterns.js +40 -0
  9. package/create-webpack-dev.d.ts +27 -0
  10. package/create-webpack-dev.d.ts.map +1 -0
  11. package/create-webpack-dev.js +151 -0
  12. package/create-webpack-prod.d.ts +28 -0
  13. package/create-webpack-prod.d.ts.map +1 -0
  14. package/create-webpack-prod.js +169 -0
  15. package/executors/build/build.impl.d.ts +19 -0
  16. package/executors/build/build.impl.d.ts.map +1 -0
  17. package/executors/build/build.impl.js +98 -0
  18. package/executors/build/schema.d.ts +35 -0
  19. package/executors/build/schema.json +135 -0
  20. package/executors/gateway/gateway.impl.d.ts +23 -0
  21. package/executors/gateway/gateway.impl.d.ts.map +1 -0
  22. package/executors/gateway/gateway.impl.js +39 -0
  23. package/executors/gateway/gateway.lib.d.ts +130 -0
  24. package/executors/gateway/gateway.lib.d.ts.map +1 -0
  25. package/executors/gateway/gateway.lib.js +797 -0
  26. package/executors/gateway/jwks-verify.d.ts +28 -0
  27. package/executors/gateway/jwks-verify.d.ts.map +1 -0
  28. package/executors/gateway/jwks-verify.js +121 -0
  29. package/executors/gateway/main.prod-gateway.d.ts +2 -0
  30. package/executors/gateway/main.prod-gateway.d.ts.map +1 -0
  31. package/executors/gateway/main.prod-gateway.js +290 -0
  32. package/executors/gateway/main.rollout.d.ts +2 -0
  33. package/executors/gateway/main.rollout.d.ts.map +1 -0
  34. package/executors/gateway/main.rollout.js +130 -0
  35. package/executors/gateway/manifest.d.ts +275 -0
  36. package/executors/gateway/manifest.d.ts.map +1 -0
  37. package/executors/gateway/manifest.js +344 -0
  38. package/executors/gateway/prod-gateway.lib.d.ts +58 -0
  39. package/executors/gateway/prod-gateway.lib.d.ts.map +1 -0
  40. package/executors/gateway/prod-gateway.lib.js +535 -0
  41. package/executors/gateway/providers-prod.d.ts +46 -0
  42. package/executors/gateway/providers-prod.d.ts.map +1 -0
  43. package/executors/gateway/providers-prod.js +199 -0
  44. package/executors/gateway/registry-source.d.ts +68 -0
  45. package/executors/gateway/registry-source.d.ts.map +1 -0
  46. package/executors/gateway/registry-source.js +131 -0
  47. package/executors/gateway/rollout-ops.d.ts +54 -0
  48. package/executors/gateway/rollout-ops.d.ts.map +1 -0
  49. package/executors/gateway/rollout-ops.js +167 -0
  50. package/executors/gateway/schema.d.ts +10 -0
  51. package/executors/gateway/schema.json +30 -0
  52. package/executors/serve/schema.d.ts +53 -0
  53. package/executors/serve/schema.json +196 -0
  54. package/executors/serve/serve.impl.d.ts +25 -0
  55. package/executors/serve/serve.impl.d.ts.map +1 -0
  56. package/executors/serve/serve.impl.js +243 -0
  57. package/executors/trust/schema.d.ts +6 -0
  58. package/executors/trust/schema.json +20 -0
  59. package/executors/trust/trust.impl.d.ts +42 -0
  60. package/executors/trust/trust.impl.d.ts.map +1 -0
  61. package/executors/trust/trust.impl.js +126 -0
  62. package/executors.json +24 -0
  63. package/index.d.ts +4 -0
  64. package/index.d.ts.map +1 -0
  65. package/index.js +9 -0
  66. package/main.devserver.d.ts +16 -0
  67. package/main.devserver.d.ts.map +1 -0
  68. package/main.devserver.js +812 -0
  69. package/package.json +66 -0
@@ -0,0 +1,53 @@
1
+ export interface WorkerEntryPoint {
2
+ name: string;
3
+ entryPath: string;
4
+ }
5
+
6
+ export interface NodeExternalsConfig {
7
+ allowlist?: string[];
8
+ additionalModuleDirs?: string[];
9
+ importType?: string;
10
+ }
11
+
12
+ export interface GatewayRoute {
13
+ /** Hostname to match - exact or `*.suffix` wildcard. */
14
+ host?: string;
15
+ /** URL prefix to match (e.g. `/api`). */
16
+ prefix?: string;
17
+ /** Proxy destination URL; omit -> this service children. */
18
+ target?: string;
19
+ /** Prepend to the forwarded URL (e.g. `/api`) - serve a prefixed app at its dev domain ROOT. */
20
+ prependPrefix?: string;
21
+ }
22
+
23
+ export interface GatewayConfig {
24
+ /** Path to a JS request-middleware module (headers injection etc.). */
25
+ middleware?: string;
26
+ /** Front-gateway routes (hostname + prefix -> target or own children). */
27
+ routes?: GatewayRoute[];
28
+ /** Terminate TLS with a cached self-signed cert (basic-ssl style). */
29
+ https?: boolean;
30
+ /** Additional listener ports with the same routing (e.g. [443] for port-less dev domains). */
31
+ extraPorts?: number[];
32
+ }
33
+
34
+ export interface ServeExecutorSchema {
35
+ entryFile: string;
36
+ tsConfigFile: string;
37
+ outputPath?: string;
38
+ assets?: string[];
39
+ workers?: WorkerEntryPoint[];
40
+ configEnv?: string;
41
+ memoryLimit?: number;
42
+ childCount?: number;
43
+ buildLibsFromSource?: boolean;
44
+ orgScopes?: string[];
45
+ bundlePackages?: string[];
46
+ nodeExternalsConfig?: NodeExternalsConfig;
47
+ webpackConfigPath?: string;
48
+ serviceName?: string;
49
+ servePrefix?: string;
50
+ gateway?: GatewayConfig;
51
+ /** Plain-http listener straight to the children - service-to-service calls, no gateway middleware. */
52
+ internalPort?: number;
53
+ }
@@ -0,0 +1,196 @@
1
+ {
2
+ "$schema": "https://json-schema.org/schema",
3
+ "$id": "DavnxWebpackServe",
4
+ "version": 2,
5
+ "outputCapture": "direct-nodejs",
6
+ "continuous": true,
7
+ "title": "NestJS Webpack Serve",
8
+ "description": "Serve a NestJS application with webpack watch mode and integrated devserver.",
9
+ "cli": "nx",
10
+ "type": "object",
11
+ "properties": {
12
+ "entryFile": {
13
+ "type": "string",
14
+ "description": "Path to the main entry-point file, relative to project root.",
15
+ "x-completion-type": "file",
16
+ "x-completion-glob": "**/*@(.js|.ts)",
17
+ "x-priority": "important",
18
+ "default": "./src/deployments/service/main.ts"
19
+ },
20
+ "tsConfigFile": {
21
+ "type": "string",
22
+ "description": "Path to the TypeScript configuration file, relative to project root.",
23
+ "x-completion-type": "file",
24
+ "x-completion-glob": "tsconfig*.json",
25
+ "x-priority": "important",
26
+ "default": "./tsconfig.app.json"
27
+ },
28
+ "outputPath": {
29
+ "type": "string",
30
+ "description": "Output directory path, relative to workspace root.",
31
+ "x-completion-type": "directory"
32
+ },
33
+ "assets": {
34
+ "type": "array",
35
+ "description": "List of static assets to copy.",
36
+ "items": {
37
+ "type": "string"
38
+ },
39
+ "default": []
40
+ },
41
+ "workers": {
42
+ "type": "array",
43
+ "description": "Worker entry points. Each produces a separate [name].js bundle compiled alongside the main entry in watch mode.",
44
+ "items": {
45
+ "type": "object",
46
+ "properties": {
47
+ "name": {
48
+ "type": "string",
49
+ "description": "Output filename (without .js extension)."
50
+ },
51
+ "entryPath": {
52
+ "type": "string",
53
+ "description": "Path to the worker entry file. './' paths are relative to project root, otherwise relative to workspace root."
54
+ }
55
+ },
56
+ "required": [
57
+ "name",
58
+ "entryPath"
59
+ ]
60
+ },
61
+ "default": []
62
+ },
63
+ "configEnv": {
64
+ "type": "string",
65
+ "description": "Environment name for config YAML resolution (config.{env}.yaml).",
66
+ "default": "development"
67
+ },
68
+ "memoryLimit": {
69
+ "type": "number",
70
+ "description": "Memory limit in MB for TypeScript type checker.",
71
+ "default": 8192
72
+ },
73
+ "childCount": {
74
+ "type": "number",
75
+ "description": "Number of child processes for the devserver.",
76
+ "default": 1
77
+ },
78
+ "buildLibsFromSource": {
79
+ "type": "boolean",
80
+ "description": "Read buildable libraries from source.",
81
+ "default": true
82
+ },
83
+ "orgScopes": {
84
+ "type": "array",
85
+ "description": "Patterns to bundle rather than externalize. Supports 4 modes: org scope ('@myorg' → @myorg/*), prefix ('@myorg/prefix' → @myorg/prefix*), regex ('/^pattern/' → custom regex), exact package name ('lodash' → lodash and lodash/subpath).",
86
+ "items": {
87
+ "type": "string"
88
+ },
89
+ "default": []
90
+ },
91
+ "bundlePackages": {
92
+ "type": "array",
93
+ "description": "Explicit package names to force-bundle into the output instead of externalizing (e.g. ['lodash', 'my-pkg']).",
94
+ "items": {
95
+ "type": "string"
96
+ },
97
+ "default": []
98
+ },
99
+ "nodeExternalsConfig": {
100
+ "type": "object",
101
+ "description": "Override options for webpack-node-externals. Merged with defaults — allowlist entries are appended to orgScopes and bundlePackages.",
102
+ "properties": {
103
+ "allowlist": {
104
+ "type": "array",
105
+ "description": "Additional patterns to allowlist (bundle instead of externalize).",
106
+ "items": {
107
+ "type": "string"
108
+ }
109
+ },
110
+ "additionalModuleDirs": {
111
+ "type": "array",
112
+ "description": "Additional directories to scan for modules to externalize.",
113
+ "items": {
114
+ "type": "string"
115
+ }
116
+ },
117
+ "importType": {
118
+ "type": "string",
119
+ "description": "Module import type for externalized packages (default: 'commonjs')."
120
+ }
121
+ }
122
+ },
123
+ "webpackConfigPath": {
124
+ "type": "string",
125
+ "description": "Path to a JS/TS file (relative to project root) that exports a (config) => config function for custom webpack overrides.",
126
+ "x-completion-type": "file",
127
+ "x-completion-glob": "webpack*@(.js|.ts)"
128
+ },
129
+ "serviceName": {
130
+ "type": "string",
131
+ "description": "Service name for config resolution and socket directory naming. Overrides the value from config YAML. Does not affect URL prefix — use servePrefix for that."
132
+ },
133
+ "servePrefix": {
134
+ "type": "string",
135
+ "description": "URL path prefix for the devserver (e.g. 'agenshield' → /agenshield/). Empty string means no prefix. Independent of serviceName.",
136
+ "default": ""
137
+ },
138
+ "gateway": {
139
+ "type": "object",
140
+ "description": "Dev gateway: pluggable request middleware, hostname/prefix routes to other dev servers, and optional basic-ssl HTTPS.",
141
+ "properties": {
142
+ "middleware": {
143
+ "type": "string",
144
+ "description": "Path to a JS file (relative to project root) that exports a function: (req: IncomingMessage, config: Record<string, unknown>) => void. Called before each request is proxied.",
145
+ "x-completion-type": "file"
146
+ },
147
+ "routes": {
148
+ "type": "array",
149
+ "description": "Front-gateway routes, matched in order by Host (exact or *.suffix wildcard) and/or URL prefix. With target -> proxied there (e.g. the vite dev server); without target -> this service's own children.",
150
+ "items": {
151
+ "type": "object",
152
+ "properties": {
153
+ "host": {
154
+ "type": "string",
155
+ "description": "Hostname to match - exact (api.tetros.localhost) or wildcard (*.tetros.localhost)."
156
+ },
157
+ "prefix": {
158
+ "type": "string",
159
+ "description": "URL prefix to match (e.g. /api)."
160
+ },
161
+ "target": {
162
+ "type": "string",
163
+ "description": "Proxy destination URL (e.g. http://localhost:5173). Omit to route to this service's children."
164
+ },
165
+ "prependPrefix": {
166
+ "type": "string",
167
+ "description": "Prepend this to the forwarded URL (e.g. /api) so a service mounted under a prefix is served at the ROOT of its dev domain — api.tetros.ai.local/tenants/... → children /api/tenants/... (prod parity with api.tetros.ai)."
168
+ }
169
+ },
170
+ "additionalProperties": false
171
+ }
172
+ },
173
+ "https": {
174
+ "type": "boolean",
175
+ "description": "Terminate TLS at the parent with a cached self-signed cert (@vitejs/plugin-basic-ssl style; SANs cover localhost, loopbacks and every route hostname). Zero config - browsers need a one-time trust confirmation."
176
+ },
177
+ "extraPorts": {
178
+ "type": "array",
179
+ "items": {
180
+ "type": "number"
181
+ },
182
+ "description": "Additional listener ports sharing the same routing/TLS — e.g. [443] so a dev domain works without :port in the URL (macOS allows unprivileged <1024 binds; failure to bind only warns)."
183
+ }
184
+ }
185
+ },
186
+ "internalPort": {
187
+ "type": "number",
188
+ "description": "Plain-http listener that proxies straight to this service children — no gateway routes, no gateway middleware (JWT). For service-to-service calls, mirroring cluster-internal networking."
189
+ }
190
+ },
191
+ "required": [
192
+ "entryFile",
193
+ "tsConfigFile"
194
+ ],
195
+ "additionalProperties": false
196
+ }
@@ -0,0 +1,25 @@
1
+ import type { ServeExecutorSchema } from './schema';
2
+ interface ExecutorContext {
3
+ root: string;
4
+ projectName?: string;
5
+ projectsConfigurations?: {
6
+ projects: Record<string, {
7
+ root: string;
8
+ }>;
9
+ };
10
+ }
11
+ /**
12
+ * Dev serve executor — webpack watch mode with integrated devserver.
13
+ *
14
+ * Owns the full devserver lifecycle:
15
+ * 1. Starts webpack in watch mode
16
+ * 2. On first successful build, forks main.devserver.js with proper env vars
17
+ * 3. On subsequent builds, POSTs /webpack/reload to devserver
18
+ * 4. On shutdown, cleans up devserver and webpack watcher
19
+ */
20
+ declare function serveExecutor(options: ServeExecutorSchema, context: ExecutorContext): AsyncGenerator<{
21
+ success: boolean;
22
+ baseUrl?: string;
23
+ }>;
24
+ export default serveExecutor;
25
+ //# sourceMappingURL=serve.impl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.impl.d.ts","sourceRoot":"","sources":["../../../../../libs/cluster/src/executors/serve/serve.impl.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAKpD,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sBAAsB,CAAC,EAAE;QACvB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAC5C,CAAC;CACH;AAED;;;;;;;;GAQG;AACH,iBAAgB,aAAa,CAC3B,OAAO,EAAE,mBAAmB,EAC5B,OAAO,EAAE,eAAe,GACvB,cAAc,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAoPxD;AAED,eAAe,aAAa,CAAC"}
@@ -0,0 +1,243 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const path = tslib_1.__importStar(require("node:path"));
5
+ const fs = tslib_1.__importStar(require("node:fs"));
6
+ const node_child_process_1 = require("node:child_process");
7
+ const yaml = tslib_1.__importStar(require("js-yaml"));
8
+ const create_webpack_dev_1 = require("../../create-webpack-dev");
9
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
10
+ const webpack = require('webpack');
11
+ /**
12
+ * Dev serve executor — webpack watch mode with integrated devserver.
13
+ *
14
+ * Owns the full devserver lifecycle:
15
+ * 1. Starts webpack in watch mode
16
+ * 2. On first successful build, forks main.devserver.js with proper env vars
17
+ * 3. On subsequent builds, POSTs /webpack/reload to devserver
18
+ * 4. On shutdown, cleans up devserver and webpack watcher
19
+ */
20
+ async function* serveExecutor(options, context) {
21
+ process.env.NODE_ENV = 'development';
22
+ const projectConfig = context.projectsConfigurations.projects[context.projectName];
23
+ const workspaceRoot = context.root;
24
+ const projectRoot = path.join(workspaceRoot, projectConfig.root);
25
+ const outputPath = options.outputPath
26
+ ? path.join(workspaceRoot, options.outputPath)
27
+ : path.join(workspaceRoot, 'dist', projectConfig.root);
28
+ // Resolve port and serviceName from config YAML
29
+ const configEnv = options.configEnv || 'development';
30
+ const configFilePath = path.join(workspaceRoot, 'config', `config.${configEnv}.yaml`);
31
+ let port = 3050;
32
+ let serviceName = context.projectName;
33
+ let yamlConfig = {};
34
+ if (fs.existsSync(configFilePath)) {
35
+ yamlConfig = yaml.load(fs.readFileSync(configFilePath, 'utf8'));
36
+ port = Number(yamlConfig.port) || port;
37
+ serviceName = yamlConfig.serviceName || serviceName;
38
+ }
39
+ if (options.serviceName) {
40
+ serviceName = options.serviceName;
41
+ }
42
+ const servePrefix = options.servePrefix ?? '';
43
+ // Resolve gateway middleware
44
+ let gatewayMiddlewarePath;
45
+ let gatewayConfigJson;
46
+ if (options.gateway?.middleware) {
47
+ gatewayMiddlewarePath = path.resolve(projectRoot, options.gateway.middleware);
48
+ gatewayConfigJson = JSON.stringify(yamlConfig);
49
+ }
50
+ // Resolve entry/tsconfig relative to project root (absolute paths avoid
51
+ // NxAppWebpackPlugin's normalizeRelativePaths collision with executor options)
52
+ const entryFile = options.entryFile || './src/deployments/service/main.ts';
53
+ const tsConfigFile = options.tsConfigFile || './tsconfig.app.json';
54
+ const resolvedMain = path.resolve(projectRoot, entryFile);
55
+ const resolvedTsConfig = path.resolve(projectRoot, tsConfigFile);
56
+ // Map workers to additional entry points
57
+ const additionalEntryPoints = (options.workers || []).map((w) => ({
58
+ entryName: w.name,
59
+ entryPath: path.resolve(projectRoot, w.entryPath),
60
+ }));
61
+ // Build dev webpack config
62
+ const config = (0, create_webpack_dev_1.createDevWebpackConfig)({
63
+ appName: context.projectName,
64
+ appRoot: projectRoot,
65
+ outputDir: outputPath,
66
+ main: resolvedMain,
67
+ tsConfig: resolvedTsConfig,
68
+ assets: options.assets || [],
69
+ additionalEntryPoints,
70
+ port,
71
+ serviceName,
72
+ memoryLimit: options.memoryLimit || 8192,
73
+ buildLibsFromSource: options.buildLibsFromSource !== false,
74
+ workspaceRoot,
75
+ orgScopes: options.orgScopes || [],
76
+ bundlePackages: options.bundlePackages || [],
77
+ nodeExternalsConfig: options.nodeExternalsConfig,
78
+ webpackConfigPath: options.webpackConfigPath,
79
+ httpsReload: options.gateway?.https === true,
80
+ });
81
+ // State
82
+ let devserverProcess = null;
83
+ let firstBuildComplete = false;
84
+ // Path to the devserver script — resolve JS first, fall back to TS (local dev with swc-node)
85
+ const devserverScriptJs = path.join(__dirname, '../../main.devserver.js');
86
+ const devserverScriptTs = path.join(__dirname, '../../main.devserver.ts');
87
+ const runningFromSource = !fs.existsSync(devserverScriptJs) && fs.existsSync(devserverScriptTs);
88
+ const devserverScript = runningFromSource ? devserverScriptTs : devserverScriptJs;
89
+ // Absolute path to the webpack bundle the devserver should load
90
+ const bundlePath = path.join(outputPath, 'main.js');
91
+ function cleanup() {
92
+ if (devserverProcess && !devserverProcess.killed) {
93
+ devserverProcess.kill('SIGTERM');
94
+ devserverProcess = null;
95
+ }
96
+ }
97
+ // Register cleanup on process signals
98
+ const signals = ['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGQUIT'];
99
+ const signalHandlers = signals.map((sig) => {
100
+ const handler = () => cleanup();
101
+ process.on(sig, handler);
102
+ return { sig, handler };
103
+ });
104
+ process.on('exit', cleanup);
105
+ function startDevServer() {
106
+ if (!fs.existsSync(devserverScript)) {
107
+ console.error(`[serve] devserver script not found at ${devserverScript}`);
108
+ return;
109
+ }
110
+ const execArgv = ['--enable-source-maps'];
111
+ if (runningFromSource) {
112
+ execArgv.unshift('--require', '@swc-node/register');
113
+ }
114
+ devserverProcess = (0, node_child_process_1.fork)(devserverScript, {
115
+ cwd: workspaceRoot,
116
+ env: {
117
+ ...process.env,
118
+ PORT: String(port),
119
+ SERVICE_NAME: serviceName,
120
+ SERVE_PREFIX: servePrefix,
121
+ // Zero-touch lens identity + bus for pod children and workers: the
122
+ // injected @lensmcp/node-instrumentation (and the grafted nest module)
123
+ // attribute every event to this project and write to the shared file
124
+ // rendezvous — without these, nest trace events fall to the console
125
+ // sink and are lost in cluster mode.
126
+ LENSMCP_PROJECT: process.env.LENSMCP_PROJECT || context.projectName || serviceName,
127
+ LENSMCP_EVENT_FILE: process.env.LENSMCP_EVENT_FILE || path.join(workspaceRoot, '.lensmcp', 'events.jsonl'),
128
+ CHILD_COUNT: String(options.childCount || process.env.CHILD_COUNT || 1),
129
+ BUNDLE_PATH: bundlePath,
130
+ ...(gatewayMiddlewarePath && { GATEWAY_MIDDLEWARE: gatewayMiddlewarePath }),
131
+ ...(gatewayConfigJson && { GATEWAY_CONFIG: gatewayConfigJson }),
132
+ ...(options.gateway?.routes?.length && { GATEWAY_ROUTES: JSON.stringify(options.gateway.routes) }),
133
+ ...(options.gateway?.https && { GATEWAY_HTTPS: "1" }),
134
+ ...(options.gateway?.extraPorts?.length && { GATEWAY_EXTRA_PORTS: JSON.stringify(options.gateway.extraPorts) }),
135
+ ...(options.internalPort && { INTERNAL_PORT: String(options.internalPort) }),
136
+ ...((options.workers?.length) && { WORKERS: JSON.stringify(options.workers.map(w => w.name)) }),
137
+ },
138
+ stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
139
+ execArgv,
140
+ });
141
+ devserverProcess.on('exit', (code, signal) => {
142
+ devserverProcess = null;
143
+ if (signal)
144
+ return; // ANY signal = intentional kill (TERM/INT/KILL) — never respawn a corpse
145
+ console.error(`[serve] devserver exited unexpectedly (code=${code}, signal=${signal}) — respawning in 1.5s`);
146
+ // Supervision: the watcher may sit idle (no rebuild event) forever —
147
+ // without this the service stays podless until a source edit.
148
+ setTimeout(() => {
149
+ if (!devserverProcess && firstBuildComplete)
150
+ startDevServer();
151
+ }, 1500).unref?.();
152
+ });
153
+ console.log(`[serve] Devserver started on port ${port} (service: ${serviceName}${servePrefix ? `, prefix: /${servePrefix}` : ''})`);
154
+ }
155
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
156
+ function _notifyDevServerReload() {
157
+ if (!devserverProcess || devserverProcess.killed) {
158
+ console.log('[serve] Devserver not running, restarting...');
159
+ startDevServer();
160
+ return;
161
+ }
162
+ fetch(`http://localhost:${port}/webpack/reload`, {
163
+ method: 'POST',
164
+ headers: { 'content-type': 'application/json' },
165
+ body: '{}',
166
+ })
167
+ .then(() => console.log('[serve] Notified devserver to reload'))
168
+ .catch((err) => {
169
+ console.warn('[serve] Failed to notify devserver, restarting...', err.message);
170
+ cleanup();
171
+ startDevServer();
172
+ });
173
+ }
174
+ // Start webpack in watch mode
175
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
176
+ const { watch: _watch, ...normalizedConfig } = config;
177
+ const compiler = webpack(normalizedConfig);
178
+ // Event queue for the async generator
179
+ let resolveNext = null;
180
+ const pendingResults = [];
181
+ function pushResult(result) {
182
+ if (resolveNext) {
183
+ const resolve = resolveNext;
184
+ resolveNext = null;
185
+ resolve(result);
186
+ }
187
+ else {
188
+ pendingResults.push(result);
189
+ }
190
+ }
191
+ function waitForNext() {
192
+ if (pendingResults.length > 0) {
193
+ return Promise.resolve(pendingResults.shift());
194
+ }
195
+ return new Promise((resolve) => { resolveNext = resolve; });
196
+ }
197
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
198
+ const watching = compiler.watch(config.watchOptions || {}, (err, stats) => {
199
+ if (err) {
200
+ console.error('[serve] Webpack error:', err);
201
+ pushResult({ success: false });
202
+ return;
203
+ }
204
+ const hasErrors = stats.hasErrors();
205
+ console.info(stats.toString({ colors: true, chunks: false, assets: true }));
206
+ if (hasErrors) {
207
+ // Don't kill the devserver — keep the last good build running
208
+ pushResult({ success: false });
209
+ return;
210
+ }
211
+ if (!firstBuildComplete || !devserverProcess) {
212
+ // first SUCCESSFUL build (a failed first compile must not strand the
213
+ // service devserver-less forever), or the devserver died while webpack
214
+ // recovered — fork it now.
215
+ firstBuildComplete = true;
216
+ startDevServer();
217
+ }
218
+ // Note: subsequent reloads are handled by DevServerReloadPlugin in webpack config
219
+ // which POSTs to /webpack/reload. We don't need to do it here since the plugin fires
220
+ // after each successful build.
221
+ pushResult({
222
+ success: true,
223
+ baseUrl: `http://localhost:${port}`,
224
+ });
225
+ });
226
+ // Main generator loop: yield each build result
227
+ try {
228
+ while (true) {
229
+ const result = await waitForNext();
230
+ yield result;
231
+ }
232
+ }
233
+ finally {
234
+ // Generator was returned/thrown (Nx killed the executor)
235
+ watching.close(() => { });
236
+ cleanup();
237
+ // Remove signal handlers
238
+ signalHandlers.forEach(({ sig, handler }) => process.removeListener(sig, handler));
239
+ }
240
+ }
241
+ exports.default = serveExecutor;
242
+ module.exports = serveExecutor;
243
+ module.exports.default = serveExecutor;
@@ -0,0 +1,6 @@
1
+ export interface TrustExecutorSchema {
2
+ /** Target whose gateway.routes hostnames should be set up. Default `serve-hmr`. */
3
+ serveTarget?: string;
4
+ /** Extra hostnames to add to /etc/hosts beyond the route hostnames. */
5
+ hosts?: string[];
6
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://json-schema.org/schema",
3
+ "version": 2,
4
+ "title": "Trust executor",
5
+ "description": "One-time local HTTPS setup for the dev gateway: trusts the davnx local CA in the OS keychain and writes /etc/hosts entries (127.0.0.1 + ::1) for every gateway route hostname. Idempotent; run from a terminal so sudo can prompt.",
6
+ "type": "object",
7
+ "properties": {
8
+ "serveTarget": {
9
+ "type": "string",
10
+ "description": "Target whose gateway.routes hostnames should be set up. Default: serve-hmr.",
11
+ "default": "serve-hmr"
12
+ },
13
+ "hosts": {
14
+ "type": "array",
15
+ "items": { "type": "string" },
16
+ "description": "Extra hostnames to add to /etc/hosts beyond the route hostnames."
17
+ }
18
+ },
19
+ "additionalProperties": false
20
+ }
@@ -0,0 +1,42 @@
1
+ import type { TrustExecutorSchema } from './schema';
2
+ interface ExecutorContext {
3
+ root: string;
4
+ projectName?: string;
5
+ projectsConfigurations?: {
6
+ projects: Record<string, {
7
+ root: string;
8
+ targets?: Record<string, {
9
+ options?: {
10
+ gateway?: {
11
+ routes?: Array<{
12
+ host?: string;
13
+ }>;
14
+ };
15
+ };
16
+ }>;
17
+ }>;
18
+ };
19
+ }
20
+ /**
21
+ * `trust` executor — one idempotent command that makes the HTTPS dev gateway
22
+ * green in real browsers:
23
+ *
24
+ * 1. mints the local dev CA if missing (same CA the gateway serves),
25
+ * 2. adds it ONCE to the macOS System keychain (`sudo security
26
+ * add-trusted-cert`) — every leaf rotation / new hostname then chains
27
+ * to it with no interstitial,
28
+ * 3. writes BOTH `/etc/hosts` families (`127.0.0.1` + `::1`) for every
29
+ * gateway route hostname — the missing `::1` line is what sends `.local`
30
+ * AAAA lookups to Bonjour/mDNS and costs ~5s per resolution,
31
+ * 4. flushes the DNS cache.
32
+ *
33
+ * Hostnames come from the project's serve target gateway routes (wildcards
34
+ * skipped — they can't live in /etc/hosts) plus any `hosts` option extras.
35
+ * Run it from a terminal: sudo prompts for your password inline.
36
+ */
37
+ export default function trustExecutor(options: TrustExecutorSchema, context: ExecutorContext): Promise<{
38
+ success: boolean;
39
+ }>;
40
+ export declare function collectHosts(options: TrustExecutorSchema, context: ExecutorContext): string[];
41
+ export {};
42
+ //# sourceMappingURL=trust.impl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust.impl.d.ts","sourceRoot":"","sources":["../../../../../libs/cluster/src/executors/trust/trust.impl.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAEpD,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sBAAsB,CAAC,EAAE;QACvB,QAAQ,EAAE,MAAM,CACd,MAAM,EACN;YACE,IAAI,EAAE,MAAM,CAAC;YACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;gBAAE,OAAO,CAAC,EAAE;oBAAE,OAAO,CAAC,EAAE;wBAAE,MAAM,CAAC,EAAE,KAAK,CAAC;4BAAE,IAAI,CAAC,EAAE,MAAM,CAAA;yBAAE,CAAC,CAAA;qBAAE,CAAA;iBAAE,CAAA;aAAE,CAAC,CAAC;SAC7F,CACF,CAAC;KACH,CAAC;CACH;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAA8B,aAAa,CACzC,OAAO,EAAE,mBAAmB,EAC5B,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAwE/B;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,eAAe,GAAG,MAAM,EAAE,CAiC7F"}