underpost 2.85.0 → 2.85.7

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.
@@ -1,108 +1,204 @@
1
- const PRE_CACHED_RESOURCES = self.renderPayload?.PRE_CACHED_RESOURCES ? self.renderPayload.PRE_CACHED_RESOURCES : [];
2
- const CACHE_NAME = self.renderPayload?.CACHE_NAME ? self.renderPayload.CACHE_NAME : 'app-cache';
3
- const PROXY_PATH = self.renderPayload?.PROXY_PATH ? self.renderPayload.PROXY_PATH : '/';
4
- self.addEventListener('install', (event) => {
5
- // Activate right away
6
- self.skipWaiting();
7
-
8
- event.waitUntil(
9
- (async () => {
10
- // Open the app's cache.
11
- const cache = await caches.open(CACHE_NAME);
12
- // Cache all static resources.
13
- try {
14
- await cache.addAll(PRE_CACHED_RESOURCES);
15
- } catch (error) {
16
- console.error(error);
17
- }
18
- // for (const cacheKey of PRE_CACHED_RESOURCES) {
19
- // try {
20
- // await cache.add(cacheKey);
21
- // } catch (error) {
22
- // console.error(error, cacheKey);
23
- // }
24
- // }
25
- })(),
26
- );
27
- });
28
-
29
- self.addEventListener('activate', (event) => {
30
- event.waitUntil(
31
- (async () => {
32
- // Enable navigation preload if it's supported.
33
- // See https://developers.google.com/web/updates/2017/02/navigation-preload
34
- if ('navigationPreload' in self.registration) {
35
- await self.registration.navigationPreload.enable();
36
- }
37
- })(),
38
- );
39
- // Tell the active service worker to take control of the page immediately.
40
- self.clients.claim();
41
- });
42
-
43
- self.addEventListener('fetch', (event) => {
44
- // Cache-First Strategy
45
- event.respondWith(
46
- (async () => {
47
- // First, try to use the navigation preload response if it's supported.
48
- try {
49
- const preloadResponse = await event.preloadResponse;
50
- if (preloadResponse) return preloadResponse;
51
- return await fetch(event.request);
52
- } catch (error) {
53
- console.error('Fetch failed; returning offline page instead.', event.request.url, error);
54
- // Fallback to the offline page.
55
- const path = PRE_CACHED_RESOURCES.find((path) => event.request.url.match(path.replaceAll('/index.html', '')));
1
+ /**
2
+ * This module provides a configurable Progressive Web App (PWA) service worker and caching strategies.
3
+ * It supports precaching assets, runtime caching with stale-while-revalidate strategy,
4
+ * and offline fallback handling.
5
+ * @module src/client/sw/default.sw.js
6
+ * @namespace PwaServiceWorker
7
+ */
8
+
9
+ /**
10
+ * Class representing a Progressive Web App (PWA) Service Worker with caching strategies.
11
+ * @class
12
+ * @memberof PwaServiceWorker
13
+ */
14
+ class PwaServiceWorker {
15
+ /**
16
+ * Initializes the service worker configuration by reading from self.renderPayload.
17
+ * If properties are not found, defaults are used.
18
+ * @constructor
19
+ * @property {Array<string>} PRE_CACHED_RESOURCES - List of resources to precache.
20
+ * @property {string} CACHE_NAME - Name of the cache storage.
21
+ * @property {string} PROXY_PATH - Base path for proxying requests.
22
+ */
23
+ constructor() {
24
+ // Configuration properties equivalent to the original global constants
25
+ this.PRE_CACHED_RESOURCES = self.renderPayload?.PRE_CACHED_RESOURCES ?? [];
26
+ this.CACHE_NAME = self.renderPayload?.CACHE_NAME ?? 'app-cache';
27
+ this.PROXY_PATH = self.renderPayload?.PROXY_PATH ?? '/';
28
+
29
+ console.log(`Service Worker Initialized. Cache: ${this.CACHE_NAME}, Proxy: ${this.PROXY_PATH}`);
30
+ }
56
31
 
32
+ /**
33
+ * Registers event listeners for the service worker lifecycle and requests.
34
+ * @method
35
+ * @memberof PwaServiceWorker
36
+ */
37
+ run() {
38
+ // Bind methods to 'this' (the instance) before attaching to self
39
+ self.addEventListener('install', this._onInstall.bind(this));
40
+ self.addEventListener('activate', this._onActivate.bind(this));
41
+ self.addEventListener('fetch', this._onFetch.bind(this));
42
+ }
43
+
44
+ /**
45
+ * Handles the 'install' event. Skips waiting and precaches static assets.
46
+ * @param {ExtendableEvent} event
47
+ * @memberof PwaServiceWorker
48
+ */
49
+ _onInstall(event) {
50
+ // Activate right away
51
+ self.skipWaiting();
52
+
53
+ event.waitUntil(
54
+ (async () => {
55
+ // Open the app's cache using the configured name.
56
+ const cache = await caches.open(this.CACHE_NAME);
57
+ // Cache all static resources.
57
58
  try {
58
- const cachedResponse = await caches.match(event.request);
59
- if (cachedResponse) return cachedResponse;
60
- const cache = await caches.open(CACHE_NAME);
61
- const preCachedResponse = await cache.match(path);
62
- if (!preCachedResponse) throw new Error(error.message);
63
- return preCachedResponse;
59
+ console.log(`Precaching ${this.PRE_CACHED_RESOURCES.length} resources...`);
60
+ await cache.addAll(this.PRE_CACHED_RESOURCES);
64
61
  } catch (error) {
65
- console.error('Error opening cache for pre cached page', {
66
- url: event.request.url,
67
- error,
68
- onLine: navigator.onLine,
69
- });
70
- try {
71
- if (!navigator.onLine) {
72
- if (event.request.method.toUpperCase() === 'GET') {
73
- const cache = await caches.open(CACHE_NAME);
74
- const preCachedResponse = await cache.match(
75
- `${PROXY_PATH === '/' ? '' : PROXY_PATH}/offline/index.html`,
76
- );
77
- if (!preCachedResponse) throw new Error(error.message);
78
- return preCachedResponse;
79
- }
80
- const response = new Response(JSON.stringify({ status: 'error', message: 'offline test response' }));
81
- // response.status = 200;
82
- response.headers.set('Content-Type', 'application/json');
83
- return response;
84
- }
62
+ console.error('Error during precaching resources:', error);
63
+ }
64
+ })(),
65
+ );
66
+ }
67
+
68
+ /**
69
+ * Handles the 'activate' event. Enables navigation preload and takes control
70
+ * of uncontrolled clients immediately.
71
+ * @param {ExtendableEvent} event
72
+ * @memberof PwaServiceWorker
73
+ */
74
+ _onActivate(event) {
75
+ event.waitUntil(
76
+ (async () => {
77
+ // Enable navigation preload if it's supported.
78
+ if ('navigationPreload' in self.registration) {
79
+ await self.registration.navigationPreload.enable();
80
+ console.log('Navigation Preload enabled.');
81
+ }
82
+ })(),
83
+ );
84
+ // Tell the active service worker to take control of the page immediately.
85
+ self.clients.claim();
86
+ }
87
+
88
+ /**
89
+ * Handles the 'fetch' event, implementing the Cache-First strategy with
90
+ * complex offline and maintenance fallbacks.
91
+ * @param {FetchEvent} event
92
+ * @memberof PwaServiceWorker
93
+ */
94
+ _onFetch(event) {
95
+ // Only handle HTTP/HTTPS requests that are not cross-origin (optional, but robust)
96
+ if (event.request.url.startsWith('http')) {
97
+ event.respondWith(this._handleFetchRequest(event));
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Core logic to handle fetching, caching, and fallbacks.
103
+ * @param {FetchEvent} event
104
+ * @returns {Promise<Response>}
105
+ * @memberof PwaServiceWorker
106
+ */
107
+ async _handleFetchRequest(event) {
108
+ // 1. Try Navigation Preload (if available) or network first
109
+ try {
110
+ const preloadResponse = await event.preloadResponse;
111
+ if (preloadResponse) return preloadResponse;
112
+
113
+ // Fall through to network request if no preload response
114
+ const networkResponse = await fetch(event.request);
115
+
116
+ // OPTIONAL: If the network request is successful, cache it for future use (stale-while-revalidate logic)
117
+ // Omitted for strict equivalence, as original only had complex fallback, not runtime caching.
118
+
119
+ return networkResponse;
120
+ } catch (error) {
121
+ console.error('Network request failed. Attempting cache/fallback logic.', event.request.url, error);
122
+
123
+ // 2. Try to match the request in the cache
124
+ try {
125
+ const cachedResponse = await caches.match(event.request);
126
+ if (cachedResponse) {
127
+ console.log(`Cache hit for: ${event.request.url}`);
128
+ return cachedResponse;
129
+ }
130
+
131
+ // 3. Try to match a precached resource path (e.g., if requesting /page, match /page/index.html)
132
+ const path = this.PRE_CACHED_RESOURCES.find((p) => event.request.url.match(p.replaceAll('/index.html', '')));
133
+
134
+ if (path) {
135
+ const cache = await caches.open(this.CACHE_NAME);
136
+ const preCachedResponse = await cache.match(path);
137
+ if (preCachedResponse) {
138
+ console.log(`Matched precached resource for: ${event.request.url} via path: ${path}`);
139
+ return preCachedResponse;
140
+ }
141
+ }
142
+
143
+ // If neither cache match nor precache path match worked, fall through to complex fallback
144
+ throw new Error('Cache miss and no precache match.');
145
+ } catch (cacheError) {
146
+ console.error('Error in primary cache lookup. Falling back to offline/maintenance pages.', {
147
+ url: event.request.url,
148
+ cacheError,
149
+ onLine: navigator.onLine,
150
+ });
151
+
152
+ // 4. Complex Fallback Logic (Offline or Maintenance)
153
+ try {
154
+ const cache = await caches.open(this.CACHE_NAME);
155
+
156
+ if (!navigator.onLine) {
157
+ // A. OFFLINE FALLBACK
85
158
  if (event.request.method.toUpperCase() === 'GET') {
86
- const cache = await caches.open(CACHE_NAME);
87
- const preCachedResponse = await cache.match(
88
- `${PROXY_PATH === '/' ? '' : PROXY_PATH}/maintenance/index.html`,
89
- );
90
- if (!preCachedResponse) throw new Error(error.message);
159
+ const offlinePath = `${this.PROXY_PATH === '/' ? '' : this.PROXY_PATH}/offline/index.html`;
160
+ const preCachedResponse = await cache.match(offlinePath);
161
+
162
+ if (!preCachedResponse) throw new Error(`Offline page not found in cache: ${offlinePath}`);
163
+
164
+ console.log('Serving offline HTML page.');
91
165
  return preCachedResponse;
92
166
  }
93
- const response = new Response(JSON.stringify({ status: 'error', message: 'server in maintenance' }));
94
- // response.status = 200;
95
- response.headers.set('Content-Type', 'application/json');
96
- return response;
97
- } catch (error) {
98
- console.error('Error opening cache for offline page', event.request.url, error);
99
- const response = new Response(JSON.stringify({ status: 'error', message: error.message }));
100
- // response.status = 200;
167
+
168
+ // B. OFFLINE API FALLBACK (Non-GET requests)
169
+ console.log('Serving offline JSON response for non-GET request.');
170
+ const response = new Response(JSON.stringify({ status: 'error', message: 'offline test response' }));
101
171
  response.headers.set('Content-Type', 'application/json');
102
172
  return response;
103
173
  }
174
+
175
+ // C. MAINTENANCE FALLBACK (Online, but network failed - interpreted as maintenance)
176
+ if (event.request.method.toUpperCase() === 'GET') {
177
+ const maintenancePath = `${this.PROXY_PATH === '/' ? '' : this.PROXY_PATH}/maintenance/index.html`;
178
+ const preCachedResponse = await cache.match(maintenancePath);
179
+
180
+ if (!preCachedResponse) throw new Error(`Maintenance page not found in cache: ${maintenancePath}`);
181
+
182
+ console.log('Serving maintenance HTML page.');
183
+ return preCachedResponse;
184
+ }
185
+
186
+ // D. MAINTENANCE API FALLBACK (Non-GET requests)
187
+ console.log('Serving maintenance JSON response for non-GET request.');
188
+ const response = new Response(JSON.stringify({ status: 'error', message: 'server in maintenance' }));
189
+ response.headers.set('Content-Type', 'application/json');
190
+ return response;
191
+ } catch (finalError) {
192
+ // 5. Final fail-safe response
193
+ console.error('Final fail-safe execution failed.', event.request.url, finalError);
194
+ const response = new Response(JSON.stringify({ status: 'error', message: finalError.message }));
195
+ response.headers.set('Content-Type', 'application/json');
196
+ return response;
104
197
  }
105
198
  }
106
- })(),
107
- );
108
- });
199
+ }
200
+ }
201
+ }
202
+
203
+ // Instantiate and run the service worker class
204
+ new PwaServiceWorker().run();
package/src/client.dev.js CHANGED
@@ -15,7 +15,7 @@ const logger = loggerFactory(import.meta);
15
15
 
16
16
  await logger.setUpInfo();
17
17
 
18
- await buildClientStaticConf();
18
+ await buildClientStaticConf({ devProxy: process.argv[6] === 'proxy' });
19
19
 
20
20
  await Config.build();
21
21
 
package/src/index.js CHANGED
@@ -35,7 +35,7 @@ class Underpost {
35
35
  * @type {String}
36
36
  * @memberof Underpost
37
37
  */
38
- static version = 'v2.85.0';
38
+ static version = 'v2.85.7';
39
39
  /**
40
40
  * Repository cli API
41
41
  * @static
package/src/proxy.js CHANGED
@@ -11,7 +11,7 @@ import { Config } from './server/conf.js';
11
11
 
12
12
  dotenv.config();
13
13
 
14
- await Config.build();
14
+ await Config.build(process.argv[2], process.argv[3], process.argv[4]);
15
15
 
16
16
  const logger = loggerFactory(import.meta);
17
17
 
@@ -23,6 +23,7 @@ import { applySecurity, authMiddlewareFactory } from '../../server/auth.js';
23
23
  import { ssrMiddlewareFactory } from '../../server/ssr.js';
24
24
  import { TLS } from '../../server/tls.js';
25
25
  import { shellExec } from '../../server/process.js';
26
+ import { devProxyHostFactory, isDevProxyContext, isTlsDevProxy } from '../../server/conf.js';
26
27
 
27
28
  const logger = loggerFactory(import.meta);
28
29
 
@@ -94,7 +95,9 @@ class ExpressService {
94
95
 
95
96
  const app = express();
96
97
 
97
- if (process.env.NODE_ENV === 'production') app.set('trust proxy', true);
98
+ if (origins && isDevProxyContext())
99
+ origins.push(devProxyHostFactory({ host, includeHttp: true, tls: isTlsDevProxy() }));
100
+ app.set('trust proxy', true);
98
101
 
99
102
  app.use((req, res, next) => {
100
103
  res.on('finish', () => {
@@ -17,6 +17,7 @@ import slowDown from 'express-slow-down';
17
17
  import cors from 'cors';
18
18
  import cookieParser from 'cookie-parser';
19
19
  import { DataBaseProvider } from '../db/DataBaseProvider.js';
20
+ import { isDevProxyContext } from './conf.js';
20
21
 
21
22
  dotenv.config();
22
23
  const logger = loggerFactory(import.meta);
@@ -348,7 +349,7 @@ const cookieOptionsFactory = (req, host) => {
348
349
  secure,
349
350
  sameSite,
350
351
  path: '/',
351
- domain: process.env.NODE_ENV === 'production' ? host : 'localhost',
352
+ domain: process.env.NODE_ENV === 'production' || isDevProxyContext() ? host : 'localhost',
352
353
  maxAge,
353
354
  };
354
355
 
@@ -66,11 +66,11 @@ const Config = {
66
66
  /**
67
67
  * @method deployIdFactory
68
68
  * @description Creates a new deploy ID.
69
- * @param {string} [deployId='dd-default'] - The deploy ID.
70
- * @param {object} [options={ cluster: false }] - The options.
69
+ * @param {string} [deployId='dd-default
70
+ * @param {object} [options={ subConf: '', cluster: false }] - The options.
71
71
  * @memberof ServerConfBuilder
72
72
  */
73
- deployIdFactory: function (deployId = 'dd-default', options = { cluster: false }) {
73
+ deployIdFactory: function (deployId = 'dd-default', options = { subConf: '', cluster: false }) {
74
74
  if (!deployId.startsWith('dd-')) deployId = `dd-${deployId}`;
75
75
 
76
76
  logger.info('Build deployId', deployId);
@@ -102,6 +102,17 @@ const Config = {
102
102
 
103
103
  this.buildTmpConf(folder);
104
104
 
105
+ if (options.subConf) {
106
+ logger.info('Creating sub conf', {
107
+ deployId: deployId,
108
+ subConf: options.subConf,
109
+ });
110
+ fs.copySync(
111
+ `./engine-private/conf/${deployId}/conf.server.json`,
112
+ `./engine-private/conf/${deployId}/conf.server.dev.${options.subConf}.json`,
113
+ );
114
+ }
115
+
105
116
  if (options.cluster === true) {
106
117
  fs.writeFileSync(
107
118
  `./.github/workflows/${repoName}.cd.yml`,
@@ -640,6 +651,8 @@ const buildProxyRouter = () => {
640
651
  for (const path of Object.keys(confServer[host])) {
641
652
  if (confServer[host][path].singleReplica) continue;
642
653
 
654
+ if (isDevProxyContext()) confServer[host][path].proxy = [isTlsDevProxy() ? 443 : 80];
655
+
643
656
  confServer[host][path].port = newInstance(currentPort);
644
657
  for (const port of confServer[host][path].proxy) {
645
658
  if (!(port in proxyRouter)) proxyRouter[port] = {};
@@ -784,14 +797,21 @@ const buildKindPorts = (from, to) =>
784
797
  /**
785
798
  * @method buildPortProxyRouter
786
799
  * @description Builds the port proxy router.
787
- * @param {number} port - The port.
788
- * @param {object} proxyRouter - The proxy router.
789
- * @param {object} [options={ orderByPathLength: false }] - The options.
800
+ * @param {object} options - The options.
801
+ * @param {number} [options.port=4000] - The port.
802
+ * @param {object} options.proxyRouter - The proxy router.
803
+ * @param {object} [options.hosts] - The hosts.
804
+ * @param {boolean} [options.orderByPathLength=false] - Whether to order by path length.
805
+ * @param {boolean} [options.devProxyContext=false] - Whether to use dev proxy context.
790
806
  * @returns {object} - The port proxy router.
791
807
  * @memberof ServerConfBuilder
792
808
  */
793
- const buildPortProxyRouter = (port, proxyRouter, options = { orderByPathLength: false }) => {
794
- const hosts = proxyRouter[port];
809
+ const buildPortProxyRouter = (
810
+ options = { port: 4000, proxyRouter, hosts, orderByPathLength: false, devProxyContext: false },
811
+ ) => {
812
+ let { port, proxyRouter, hosts, orderByPathLength } = options;
813
+ hosts = hosts || proxyRouter[port] || {};
814
+
795
815
  const router = {};
796
816
  // build router
797
817
  Object.keys(hosts).map((hostKey) => {
@@ -804,7 +824,7 @@ const buildPortProxyRouter = (port, proxyRouter, options = { orderByPathLength:
804
824
  return;
805
825
  }
806
826
  }
807
-
827
+ // ${process.env.NODE_ENV === 'development' && !isDevProxyContext() ? `:${port}` : ''}
808
828
  const absoluteHost = [80, 443].includes(port)
809
829
  ? `${host}${path === '/' ? '' : path}`
810
830
  : `${host}:${port}${path === '/' ? '' : path}`;
@@ -812,12 +832,48 @@ const buildPortProxyRouter = (port, proxyRouter, options = { orderByPathLength:
812
832
  if (absoluteHost in router)
813
833
  logger.warn('Overwrite: Absolute host already exists on router', { absoluteHost, target });
814
834
 
815
- router[absoluteHost] = target;
835
+ if (options.devProxyContext === true) {
836
+ const appDevPort = parseInt(target.split(':')[2]) - process.env.DEV_PROXY_PORT_OFFSET;
837
+ router[absoluteHost] = `http://localhost:${appDevPort}`;
838
+ } else router[absoluteHost] = target;
816
839
  }); // order router
817
840
 
818
841
  if (Object.keys(router).length === 0) return router;
819
842
 
820
- if (options.orderByPathLength === true) {
843
+ if (options.devProxyContext === true && process.env.NODE_ENV === 'development') {
844
+ const confDevApiServer = JSON.parse(
845
+ fs.readFileSync(`./engine-private/conf/${process.argv[3]}/conf.server.dev.${process.argv[4]}-dev-api.json`),
846
+ 'utf8',
847
+ );
848
+ let devApiHosts = [];
849
+ let origins = [];
850
+ for (const _host of Object.keys(confDevApiServer))
851
+ for (const _path of Object.keys(confDevApiServer[_host])) {
852
+ if (confDevApiServer[_host][_path].origins && confDevApiServer[_host][_path].origins.length) {
853
+ origins.push(...confDevApiServer[_host][_path].origins);
854
+ if (_path !== 'peer' && devApiHosts.length === 0)
855
+ devApiHosts.push(
856
+ `${_host}${[80, 443].includes(port) && isDevProxyContext() ? '' : `:${port}`}${_path == '/' ? '' : _path}`,
857
+ );
858
+ }
859
+ }
860
+ origins = Array.from(new Set(origins));
861
+ console.log({
862
+ origins,
863
+ devApiHosts,
864
+ });
865
+ for (const devApiHost of devApiHosts) {
866
+ if (devApiHost in router) {
867
+ const target = router[devApiHost];
868
+ delete router[devApiHost];
869
+ router[`${devApiHost}/${process.env.BASE_API}`] = target;
870
+ router[`${devApiHost}/socket.io`] = target;
871
+ for (const origin of origins) router[devApiHost] = origin;
872
+ }
873
+ }
874
+ }
875
+
876
+ if (orderByPathLength === true) {
821
877
  const reOrderRouter = {};
822
878
  for (const absoluteHostKey of orderArrayFromAttrInt(Object.keys(router), 'length'))
823
879
  reOrderRouter[absoluteHostKey] = router[absoluteHostKey];
@@ -1409,11 +1465,14 @@ const buildApiConf = async (options = { deployId: '', subConf: '', host: '', pat
1409
1465
  * @param {string} options.apiBaseHost - The API base host.
1410
1466
  * @param {string} options.host - The host.
1411
1467
  * @param {string} options.path - The path.
1468
+ * @param {boolean} options.devProxy - The dev proxy flag.
1412
1469
  * @returns {void}
1413
1470
  * @memberof ServerConfBuilder
1414
1471
  */
1415
- const buildClientStaticConf = async (options = { deployId: '', subConf: '', apiBaseHost: '', host: '', path: '' }) => {
1416
- let { deployId, subConf, host, path } = options;
1472
+ const buildClientStaticConf = async (
1473
+ options = { deployId: '', subConf: '', apiBaseHost: '', host: '', path: '', devProxy: false },
1474
+ ) => {
1475
+ let { deployId, subConf, host, path, devProxy } = options;
1417
1476
  if (!deployId) deployId = process.argv[2].trim();
1418
1477
  if (!subConf) subConf = process.argv[3].trim();
1419
1478
  if (!host) host = process.argv[4].trim();
@@ -1425,10 +1484,14 @@ const buildClientStaticConf = async (options = { deployId: '', subConf: '', apiB
1425
1484
  fs.readFileSync(`./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${subConf}-dev-api`, 'utf8'),
1426
1485
  );
1427
1486
  envObj.PORT = parseInt(envObj.PORT);
1428
- const apiBaseHost = options?.apiBaseHost ? options.apiBaseHost : `localhost:${envObj.PORT + 1}`;
1487
+ const apiBaseHost = devProxy
1488
+ ? devProxyHostFactory({ host, tls: isTlsDevProxy() })
1489
+ : options?.apiBaseHost
1490
+ ? options.apiBaseHost
1491
+ : `localhost:${envObj.PORT + 1}`;
1429
1492
  confServer[host][path].apiBaseHost = apiBaseHost;
1430
1493
  confServer[host][path].apiBaseProxyPath = path;
1431
- logger.info('Build client static conf', { host, path, apiBaseHost });
1494
+ logger.warn('Build client static conf', { host, path, apiBaseHost });
1432
1495
  envObj.PORT = parseInt(confServer[host][path].origins[0].split(':')[2]) - 1;
1433
1496
  writeEnv(`./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${subConf}-dev-client`, envObj);
1434
1497
  fs.writeFileSync(
@@ -1448,6 +1511,56 @@ const buildClientStaticConf = async (options = { deployId: '', subConf: '', apiB
1448
1511
  */
1449
1512
  const isDeployRunnerContext = (path, options) => !options.build && path && path !== 'template-deploy';
1450
1513
 
1514
+ /**
1515
+ * @method isDevProxyContext
1516
+ * @description Checks if the dev proxy context is valid.
1517
+ * @returns {boolean} - The dev proxy context.
1518
+ * @memberof ServerConfBuilder
1519
+ */
1520
+ const isDevProxyContext = () => {
1521
+ try {
1522
+ if (process.argv[2] === 'proxy') return true;
1523
+ if (!process.argv[6].startsWith('localhost')) return false;
1524
+ return new URL('http://' + process.argv[6]).hostname ? true : false;
1525
+ } catch {
1526
+ return false;
1527
+ }
1528
+ };
1529
+
1530
+ /**
1531
+ * @method devProxyHostFactory
1532
+ * @description Creates the dev proxy host.
1533
+ * @param {object} options - The options.
1534
+ * @param {string} [options.host='default.net'] - The host.
1535
+ * @param {boolean} [options.includeHttp=false] - Whether to include HTTP.
1536
+ * @param {number} [options.port=443] - The port.
1537
+ * @param {boolean} [options.tls=false] - Whether to use TLS.
1538
+ * @returns {string} - The dev proxy host.
1539
+ * @memberof ServerConfBuilder
1540
+ */
1541
+ const devProxyHostFactory = (options = { host: 'default.net', includeHttp: false, port: 80, tls: false }) =>
1542
+ `${options.includeHttp ? (options.tls ? 'https://' : 'http://') : ''}${options.host ? options.host : 'localhost'}:${
1543
+ (options.port ? options.port : options.tls ? 443 : 80) + parseInt(process.env.DEV_PROXY_PORT_OFFSET)
1544
+ }`;
1545
+
1546
+ /**
1547
+ * @method isTlsDevProxy
1548
+ * @description Checks if TLS is used in the dev proxy.
1549
+ * @returns {boolean} - The TLS dev proxy status.
1550
+ * @memberof ServerConfBuilder
1551
+ */
1552
+ const isTlsDevProxy = () => process.env.NODE_ENV !== 'production' && process.argv[7] === 'tls';
1553
+
1554
+ /**
1555
+ * @method getTlsHosts
1556
+ * @description Gets the TLS hosts.
1557
+ * @param {object} confServer - The server configuration.
1558
+ * @returns {Array} - The TLS hosts.
1559
+ * @memberof ServerConfBuilder
1560
+ */
1561
+ const getTlsHosts = (confServer) =>
1562
+ Array.from(new Set(Object.keys(confServer).map((h) => new URL('https://' + h).hostname)));
1563
+
1451
1564
  export {
1452
1565
  Cmd,
1453
1566
  Config,
@@ -1484,4 +1597,8 @@ export {
1484
1597
  buildApiConf,
1485
1598
  buildClientStaticConf,
1486
1599
  isDeployRunnerContext,
1600
+ isDevProxyContext,
1601
+ devProxyHostFactory,
1602
+ isTlsDevProxy,
1603
+ getTlsHosts,
1487
1604
  };