@sap-ux/preview-middleware 0.23.9 → 0.23.11
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/README.md +10 -1
- package/dist/base/index.d.ts +1 -0
- package/dist/base/index.js +4 -1
- package/dist/base/remote-url.d.ts +33 -0
- package/dist/base/remote-url.js +208 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -1
- package/dist/ui5/middleware.js +4 -0
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -177,8 +177,17 @@ When the middleware is used in an adaptation project together with a middleware
|
|
|
177
177
|
afterMiddleware: preview-middleware
|
|
178
178
|
```
|
|
179
179
|
|
|
180
|
+
### [Mobile Device Preview](#mobile-device-preview)
|
|
181
|
+
The preview middleware supports previewing applications on physical mobile devices, enabling developers to test their applications on real mobile devices directly from Visual Studio Code or SAP Business Application Studio.
|
|
182
|
+
|
|
183
|
+
Using the `--accept-remote-connections` argument, a remote URL that can be accessed from mobile devices on the same network will be logged in the console, and a QR code will be displayed for easy access.
|
|
184
|
+
|
|
185
|
+
```Json
|
|
186
|
+
"start-mobile": "ui5 serve --open test/flp.html#app-preview --accept-remote-connections"
|
|
187
|
+
```
|
|
188
|
+
|
|
180
189
|
### [Programmatic Usage](#programmatic-usage)
|
|
181
|
-
Alternatively you can use the underlying middleware
|
|
190
|
+
Alternatively you can use the underlying middleware function programmatically. This is useful when you want to incorporate the `preview-middleware` functionality in your own middleware.
|
|
182
191
|
|
|
183
192
|
```typescript
|
|
184
193
|
import { FlpSandbox } from '@sap-ux/preview-middleware';
|
package/dist/base/index.d.ts
CHANGED
package/dist/base/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sanitizeRtaConfig = exports.getPreviewPaths = exports.generatePreviewFiles = exports.initAdp = exports.FlpSandbox = void 0;
|
|
3
|
+
exports.isRemoteConnectionsEnabled = exports.logRemoteUrl = exports.sanitizeRtaConfig = exports.getPreviewPaths = exports.generatePreviewFiles = exports.initAdp = exports.FlpSandbox = void 0;
|
|
4
4
|
var flp_1 = require("./flp");
|
|
5
5
|
Object.defineProperty(exports, "FlpSandbox", { enumerable: true, get: function () { return flp_1.FlpSandbox; } });
|
|
6
6
|
Object.defineProperty(exports, "initAdp", { enumerable: true, get: function () { return flp_1.initAdp; } });
|
|
@@ -8,4 +8,7 @@ var config_1 = require("./config");
|
|
|
8
8
|
Object.defineProperty(exports, "generatePreviewFiles", { enumerable: true, get: function () { return config_1.generatePreviewFiles; } });
|
|
9
9
|
Object.defineProperty(exports, "getPreviewPaths", { enumerable: true, get: function () { return config_1.getPreviewPaths; } });
|
|
10
10
|
Object.defineProperty(exports, "sanitizeRtaConfig", { enumerable: true, get: function () { return config_1.sanitizeRtaConfig; } });
|
|
11
|
+
var remote_url_1 = require("./remote-url");
|
|
12
|
+
Object.defineProperty(exports, "logRemoteUrl", { enumerable: true, get: function () { return remote_url_1.logRemoteUrl; } });
|
|
13
|
+
Object.defineProperty(exports, "isRemoteConnectionsEnabled", { enumerable: true, get: function () { return remote_url_1.isRemoteConnectionsEnabled; } });
|
|
11
14
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ToolsLogger } from '@sap-ux/logger';
|
|
2
|
+
/**
|
|
3
|
+
* Log remote URL for mobile device access.
|
|
4
|
+
*
|
|
5
|
+
* @param logger ToolsLogger instance
|
|
6
|
+
*/
|
|
7
|
+
export declare function logRemoteUrl(logger: ToolsLogger): Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Get the remote URL for mobile device access.
|
|
10
|
+
*
|
|
11
|
+
* @param logger ToolsLogger instance
|
|
12
|
+
* @returns The remote URL or undefined if not available
|
|
13
|
+
*/
|
|
14
|
+
export declare function getRemoteUrl(logger: ToolsLogger): Promise<string | undefined>;
|
|
15
|
+
/**
|
|
16
|
+
* Check if remote connections should be enabled based on command line arguments.
|
|
17
|
+
*
|
|
18
|
+
* @returns Whether --accept-remote-connections is present in process arguments
|
|
19
|
+
*/
|
|
20
|
+
export declare function isRemoteConnectionsEnabled(): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Extract port from command line arguments or environment.
|
|
23
|
+
*
|
|
24
|
+
* @returns The port number if found
|
|
25
|
+
*/
|
|
26
|
+
export declare function getPortFromArgs(): number | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Extract open path from command line arguments.
|
|
29
|
+
*
|
|
30
|
+
* @returns The path from --open or -o parameter if found
|
|
31
|
+
*/
|
|
32
|
+
export declare function getOpenPathFromArgs(): string | undefined;
|
|
33
|
+
//# sourceMappingURL=remote-url.d.ts.map
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.logRemoteUrl = logRemoteUrl;
|
|
7
|
+
exports.getRemoteUrl = getRemoteUrl;
|
|
8
|
+
exports.isRemoteConnectionsEnabled = isRemoteConnectionsEnabled;
|
|
9
|
+
exports.getPortFromArgs = getPortFromArgs;
|
|
10
|
+
exports.getOpenPathFromArgs = getOpenPathFromArgs;
|
|
11
|
+
const btp_utils_1 = require("@sap-ux/btp-utils");
|
|
12
|
+
const os_1 = require("os");
|
|
13
|
+
const bas_sdk_1 = require("@sap/bas-sdk");
|
|
14
|
+
const qrcode_1 = __importDefault(require("qrcode"));
|
|
15
|
+
/**
|
|
16
|
+
* Log remote URL for mobile device access.
|
|
17
|
+
*
|
|
18
|
+
* @param logger ToolsLogger instance
|
|
19
|
+
*/
|
|
20
|
+
async function logRemoteUrl(logger) {
|
|
21
|
+
try {
|
|
22
|
+
const remoteUrl = await getRemoteUrl(logger);
|
|
23
|
+
const generateQRCode = async (text) => {
|
|
24
|
+
try {
|
|
25
|
+
const qrString = await qrcode_1.default.toString(text, { type: 'terminal', small: true });
|
|
26
|
+
logger.info(qrString);
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
logger.error(err);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
if (remoteUrl) {
|
|
33
|
+
logger.info(`Remote URL: ${remoteUrl}`);
|
|
34
|
+
logger.info('Scan the QR code below with your mobile device to access the preview:');
|
|
35
|
+
await generateQRCode(remoteUrl);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
logger.debug(`Could not generate remote URL: ${error.message}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get the remote URL for mobile device access.
|
|
44
|
+
*
|
|
45
|
+
* @param logger ToolsLogger instance
|
|
46
|
+
* @returns The remote URL or undefined if not available
|
|
47
|
+
*/
|
|
48
|
+
async function getRemoteUrl(logger) {
|
|
49
|
+
try {
|
|
50
|
+
if ((0, btp_utils_1.isAppStudio)()) {
|
|
51
|
+
return await getBASRemoteUrl(logger);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
return getIdeRemoteUrl(logger);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
logger.error(`Failed to generate remote URL: ${error.message}`);
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get remote URL for BAS environment using BAS SDK.
|
|
64
|
+
*
|
|
65
|
+
* @param logger ToolsLogger instance instance
|
|
66
|
+
* @returns The remote URL from BAS SDK
|
|
67
|
+
*/
|
|
68
|
+
async function getBASRemoteUrl(logger) {
|
|
69
|
+
try {
|
|
70
|
+
const devspaceInfo = await bas_sdk_1.devspace.getDevspaceInfo();
|
|
71
|
+
if (devspaceInfo?.url) {
|
|
72
|
+
const port = getPortFromArgs() ?? 8080;
|
|
73
|
+
const baseUrl = bas_sdk_1.devspace.getAppExternalUri(`http://localhost:${port}`);
|
|
74
|
+
const devspaceUrl = baseUrl.replace(/(\/|%2F)$/, '');
|
|
75
|
+
const remoteUrl = appendOpenPath(devspaceUrl, getOpenPathFromArgs());
|
|
76
|
+
logger.debug(`BAS remote URL generated: ${remoteUrl}`);
|
|
77
|
+
return remoteUrl;
|
|
78
|
+
}
|
|
79
|
+
logger.warn('Could not retrieve BAS remote URL from devspace info');
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
logger.error(`Failed to get BAS remote URL: ${error.message}`);
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get remote URL for the local IDE environment by detecting network IP.
|
|
89
|
+
* Only works if --accept-remote-connections is enabled.
|
|
90
|
+
*
|
|
91
|
+
* @param logger ToolsLogger instance
|
|
92
|
+
* @returns The remote URL based on network IP
|
|
93
|
+
*/
|
|
94
|
+
function getIdeRemoteUrl(logger) {
|
|
95
|
+
try {
|
|
96
|
+
const networkIP = getNetworkIP();
|
|
97
|
+
if (!networkIP) {
|
|
98
|
+
logger.warn('Could not determine network IP address for remote access');
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
const protocol = 'http';
|
|
102
|
+
const port = getPortFromArgs() ?? 8080;
|
|
103
|
+
const baseUrl = `${protocol}://${networkIP}:${port}`;
|
|
104
|
+
const remoteUrl = appendOpenPath(baseUrl, getOpenPathFromArgs());
|
|
105
|
+
logger.debug(`IDE remote URL generated: ${remoteUrl}`);
|
|
106
|
+
return remoteUrl;
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
logger.error(`Failed to generate IDE remote URL: ${error.message}`);
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Get the first available network IP address (excluding localhost).
|
|
115
|
+
*
|
|
116
|
+
* @returns The network IP address
|
|
117
|
+
*/
|
|
118
|
+
function getNetworkIP() {
|
|
119
|
+
const interfaces = (0, os_1.networkInterfaces)();
|
|
120
|
+
// Priority order: prefer non-internal IPv4 addresses
|
|
121
|
+
for (const interfaceName of Object.keys(interfaces)) {
|
|
122
|
+
const networkInterface = interfaces[interfaceName];
|
|
123
|
+
if (!networkInterface) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
for (const alias of networkInterface) {
|
|
127
|
+
// Skip internal (localhost) addresses and IPv6
|
|
128
|
+
if (alias.family === 'IPv4' && !alias.internal) {
|
|
129
|
+
return alias.address;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Append the open path to a base URL if provided.
|
|
137
|
+
*
|
|
138
|
+
* @param baseUrl The base URL
|
|
139
|
+
* @param openPath The path to append
|
|
140
|
+
* @returns The complete URL with path appended
|
|
141
|
+
*/
|
|
142
|
+
function appendOpenPath(baseUrl, openPath) {
|
|
143
|
+
if (!openPath) {
|
|
144
|
+
return baseUrl;
|
|
145
|
+
}
|
|
146
|
+
// Ensure the path starts with a forward slash
|
|
147
|
+
const normalizedPath = openPath.startsWith('/') ? openPath : `/${openPath}`;
|
|
148
|
+
return `${baseUrl}${normalizedPath}`;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Check if remote connections should be enabled based on command line arguments.
|
|
152
|
+
*
|
|
153
|
+
* @returns Whether --accept-remote-connections is present in process arguments
|
|
154
|
+
*/
|
|
155
|
+
function isRemoteConnectionsEnabled() {
|
|
156
|
+
return process.argv.includes('--accept-remote-connections');
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Extract port from command line arguments or environment.
|
|
160
|
+
*
|
|
161
|
+
* @returns The port number if found
|
|
162
|
+
*/
|
|
163
|
+
function getPortFromArgs() {
|
|
164
|
+
// Check for --port argument
|
|
165
|
+
const portIndex = process.argv.findIndex((arg) => arg === '--port' || arg.startsWith('--port='));
|
|
166
|
+
if (portIndex !== -1) {
|
|
167
|
+
const portArg = process.argv[portIndex];
|
|
168
|
+
if (portArg.includes('=')) {
|
|
169
|
+
// --port=8080 format
|
|
170
|
+
const port = parseInt(portArg.split('=')[1], 10);
|
|
171
|
+
return isNaN(port) ? undefined : port;
|
|
172
|
+
}
|
|
173
|
+
else if (portIndex + 1 < process.argv.length) {
|
|
174
|
+
// --port 8080 format
|
|
175
|
+
const port = parseInt(process.argv[portIndex + 1], 10);
|
|
176
|
+
return isNaN(port) ? undefined : port;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Check environment variable
|
|
180
|
+
const envPort = process.env.PORT;
|
|
181
|
+
if (envPort) {
|
|
182
|
+
const port = parseInt(envPort, 10);
|
|
183
|
+
return isNaN(port) ? undefined : port;
|
|
184
|
+
}
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Extract open path from command line arguments.
|
|
189
|
+
*
|
|
190
|
+
* @returns The path from --open or -o parameter if found
|
|
191
|
+
*/
|
|
192
|
+
function getOpenPathFromArgs() {
|
|
193
|
+
// Check for --open or -o argument
|
|
194
|
+
const openIndex = process.argv.findIndex((arg) => arg === '--open' || arg === '-o' || arg.startsWith('--open=') || arg.startsWith('--o='));
|
|
195
|
+
if (openIndex !== -1) {
|
|
196
|
+
const openArg = process.argv[openIndex];
|
|
197
|
+
if (openArg.includes('=')) {
|
|
198
|
+
// --open=path or -o=path format
|
|
199
|
+
return openArg.split('=')[1];
|
|
200
|
+
}
|
|
201
|
+
else if (openIndex + 1 < process.argv.length) {
|
|
202
|
+
// --open path or -o path format
|
|
203
|
+
return process.argv[openIndex + 1];
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=remote-url.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export * from './ui5/middleware';
|
|
2
|
-
export { FlpSandbox, initAdp, generatePreviewFiles, getPreviewPaths, sanitizeRtaConfig } from './base';
|
|
2
|
+
export { FlpSandbox, initAdp, generatePreviewFiles, getPreviewPaths, sanitizeRtaConfig, logRemoteUrl, isRemoteConnectionsEnabled } from './base';
|
|
3
3
|
export { FlpConfig, RtaConfig, TestConfig, MiddlewareConfig, DefaultFlpPath, DefaultIntent, TestConfigDefaults } from './types';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.sanitizeRtaConfig = exports.getPreviewPaths = exports.generatePreviewFiles = exports.initAdp = exports.FlpSandbox = void 0;
|
|
17
|
+
exports.isRemoteConnectionsEnabled = exports.logRemoteUrl = exports.sanitizeRtaConfig = exports.getPreviewPaths = exports.generatePreviewFiles = exports.initAdp = exports.FlpSandbox = void 0;
|
|
18
18
|
__exportStar(require("./ui5/middleware"), exports);
|
|
19
19
|
var base_1 = require("./base");
|
|
20
20
|
Object.defineProperty(exports, "FlpSandbox", { enumerable: true, get: function () { return base_1.FlpSandbox; } });
|
|
@@ -22,4 +22,6 @@ Object.defineProperty(exports, "initAdp", { enumerable: true, get: function () {
|
|
|
22
22
|
Object.defineProperty(exports, "generatePreviewFiles", { enumerable: true, get: function () { return base_1.generatePreviewFiles; } });
|
|
23
23
|
Object.defineProperty(exports, "getPreviewPaths", { enumerable: true, get: function () { return base_1.getPreviewPaths; } });
|
|
24
24
|
Object.defineProperty(exports, "sanitizeRtaConfig", { enumerable: true, get: function () { return base_1.sanitizeRtaConfig; } });
|
|
25
|
+
Object.defineProperty(exports, "logRemoteUrl", { enumerable: true, get: function () { return base_1.logRemoteUrl; } });
|
|
26
|
+
Object.defineProperty(exports, "isRemoteConnectionsEnabled", { enumerable: true, get: function () { return base_1.isRemoteConnectionsEnabled; } });
|
|
25
27
|
//# sourceMappingURL=index.js.map
|
package/dist/ui5/middleware.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const logger_1 = require("@sap-ux/logger");
|
|
4
4
|
const flp_1 = require("../base/flp");
|
|
5
5
|
const config_1 = require("../base/config");
|
|
6
|
+
const remote_url_1 = require("../base/remote-url");
|
|
6
7
|
/**
|
|
7
8
|
* Create the router that is to be exposed as UI5 middleware.
|
|
8
9
|
*
|
|
@@ -33,6 +34,9 @@ async function createRouter({ resources, options, middlewareUtil }, logger) {
|
|
|
33
34
|
throw new Error('No manifest.json found.');
|
|
34
35
|
}
|
|
35
36
|
}
|
|
37
|
+
if ((0, remote_url_1.isRemoteConnectionsEnabled)()) {
|
|
38
|
+
await (0, remote_url_1.logRemoteUrl)(logger);
|
|
39
|
+
}
|
|
36
40
|
// add exposed endpoints for cds-plugin-ui5
|
|
37
41
|
flp.router.getAppPages = () => (0, config_1.getPreviewPaths)(config).map(({ path }) => path);
|
|
38
42
|
return flp.router;
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"bugs": {
|
|
10
10
|
"url": "https://github.com/SAP/open-ux-tools/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Apreview-middleware"
|
|
11
11
|
},
|
|
12
|
-
"version": "0.23.
|
|
12
|
+
"version": "0.23.11",
|
|
13
13
|
"license": "Apache-2.0",
|
|
14
14
|
"author": "@SAP/ux-tools-team",
|
|
15
15
|
"main": "dist/index.js",
|
|
@@ -25,18 +25,21 @@
|
|
|
25
25
|
"ejs": "3.1.10",
|
|
26
26
|
"mem-fs": "2.1.0",
|
|
27
27
|
"mem-fs-editor": "9.4.0",
|
|
28
|
-
"
|
|
28
|
+
"qrcode": "1.5.4",
|
|
29
|
+
"@sap/bas-sdk": "3.11.6",
|
|
30
|
+
"@sap-ux/adp-tooling": "0.15.28",
|
|
29
31
|
"@sap-ux/btp-utils": "1.1.1",
|
|
30
32
|
"@sap-ux/control-property-editor-sources": "npm:@sap-ux/control-property-editor@0.7.0",
|
|
31
33
|
"@sap-ux/feature-toggle": "0.3.1",
|
|
32
34
|
"@sap-ux/logger": "0.7.0",
|
|
33
|
-
"@sap-ux/project-access": "1.
|
|
35
|
+
"@sap-ux/project-access": "1.31.0",
|
|
34
36
|
"@sap-ux/system-access": "0.6.16",
|
|
35
37
|
"@sap-ux/i18n": "0.3.3"
|
|
36
38
|
},
|
|
37
39
|
"devDependencies": {
|
|
38
40
|
"@sap-ux-private/playwright": "0.2.0",
|
|
39
41
|
"@types/connect": "^3.4.38",
|
|
42
|
+
"@types/qrcode": "1.5.5",
|
|
40
43
|
"@types/ejs": "3.1.2",
|
|
41
44
|
"@types/express": "4.17.21",
|
|
42
45
|
"@types/mem-fs": "1.1.2",
|