@sasjs/cli 6.30.1 → 6.32.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.
- package/build/commands/build/internal/mockSite/assets/api-console.css +365 -0
- package/build/commands/build/internal/mockSite/assets/api-console.js +797 -0
- package/build/commands/build/internal/mockSite/assets/api-index.html +83 -0
- package/build/commands/build/internal/mockSite/assets/sw.js +5 -4
- package/build/commands/build/internal/mockSite/mockSite.d.ts +2 -0
- package/build/commands/build/internal/mockSite/mockSite.js +84 -7
- package/build/commands/build/internal/mockSite/spec/apiConsole.spec.d.ts +1 -0
- package/build/commands/build/internal/mockSite/spec/apiConsole.spec.js +1437 -0
- package/build/commands/build/internal/mockSite/spec/apiConsoleBasePath.spec.d.ts +1 -0
- package/build/commands/build/internal/mockSite/spec/apiConsoleBasePath.spec.js +415 -0
- package/build/commands/build/internal/mockSite/spec/mockSite.spec.js +84 -0
- package/build/commands/build/internal/mockSite/spec/sw.spec.js +28 -0
- package/build/commands/version/spec/version.spec.js +11 -1
- package/build/commands/version/version.d.ts +5 -3
- package/build/commands/version/version.js +17 -7
- package/build/types/mockWebBasePath.d.ts +9 -0
- package/package.json +13 -32
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>API console - browser demo</title>
|
|
7
|
+
<meta name="sasjs-mock-version" content="__MOCK_VERSION__" />
|
|
8
|
+
<!--
|
|
9
|
+
The API console for a browser-hosted build.
|
|
10
|
+
|
|
11
|
+
Static, and deliberately so: no bundler, no framework, no network of its
|
|
12
|
+
own beyond the API it is documenting. It is served from <base>/api-console/ and
|
|
13
|
+
every request it makes is the same request an application makes, so the
|
|
14
|
+
service worker answers it and the answer is the mock's real answer.
|
|
15
|
+
|
|
16
|
+
Nothing is inline: a project that sets `default-src 'self'` without
|
|
17
|
+
'unsafe-inline' would refuse an inline script or style, and this page
|
|
18
|
+
cannot add a hash to a policy it does not own.
|
|
19
|
+
-->
|
|
20
|
+
<link rel="stylesheet" href="console.css" />
|
|
21
|
+
</head>
|
|
22
|
+
<body>
|
|
23
|
+
<header class="bar">
|
|
24
|
+
<h1>API console</h1>
|
|
25
|
+
<span class="badge">browser demo</span>
|
|
26
|
+
<span class="spacer"></span>
|
|
27
|
+
<span id="who" class="who" title="The account this host answers as"
|
|
28
|
+
>connected</span
|
|
29
|
+
>
|
|
30
|
+
</header>
|
|
31
|
+
|
|
32
|
+
<main class="layout">
|
|
33
|
+
<aside class="list">
|
|
34
|
+
<input
|
|
35
|
+
id="filter"
|
|
36
|
+
type="search"
|
|
37
|
+
placeholder="Filter routes (user, group, drive...)"
|
|
38
|
+
autocomplete="off"
|
|
39
|
+
/>
|
|
40
|
+
<div id="routes" class="routes"></div>
|
|
41
|
+
</aside>
|
|
42
|
+
|
|
43
|
+
<section class="detail">
|
|
44
|
+
<div id="empty" class="empty">
|
|
45
|
+
<p>
|
|
46
|
+
This is a <strong>browser-hosted</strong> SASjs server: the API you
|
|
47
|
+
are about to call runs in a service worker in this tab, and its
|
|
48
|
+
drive lives in this browser.
|
|
49
|
+
</p>
|
|
50
|
+
<p>
|
|
51
|
+
Pick a route on the left. Writes are allowed on purpose, and they
|
|
52
|
+
survive a refresh - add <code>?reset=1</code> to this page's URL to
|
|
53
|
+
clear the drive and start again from the shipped state.
|
|
54
|
+
</p>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<!-- outside #empty: the accounts are most needed while a route is open,
|
|
58
|
+
and #empty is hidden as soon as one is selected -->
|
|
59
|
+
<div id="accounts"></div>
|
|
60
|
+
|
|
61
|
+
<div id="route" hidden>
|
|
62
|
+
<h2 id="title"></h2>
|
|
63
|
+
<p id="summary" class="summary"></p>
|
|
64
|
+
<form id="form" class="form"></form>
|
|
65
|
+
<div class="actions">
|
|
66
|
+
<button id="send" type="button">Send</button>
|
|
67
|
+
<span id="timing" class="timing"></span>
|
|
68
|
+
</div>
|
|
69
|
+
<div id="response" class="response" hidden>
|
|
70
|
+
<div class="responsebar">
|
|
71
|
+
<span id="status" class="status"></span>
|
|
72
|
+
<span class="spacer"></span>
|
|
73
|
+
<button id="copy" type="button" class="link">Copy</button>
|
|
74
|
+
</div>
|
|
75
|
+
<pre id="body"></pre>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</section>
|
|
79
|
+
</main>
|
|
80
|
+
|
|
81
|
+
<script src="console.js"></script>
|
|
82
|
+
</body>
|
|
83
|
+
</html>
|
|
@@ -1764,10 +1764,11 @@ self.addEventListener('fetch', (event) => {
|
|
|
1764
1764
|
// listing is the server's web route, and an interface that lists apps asks for
|
|
1765
1765
|
// it on every visit.
|
|
1766
1766
|
//
|
|
1767
|
-
// `/api` and `/api/` on their own are
|
|
1768
|
-
//
|
|
1769
|
-
// and the
|
|
1770
|
-
// short spelling, which routeRequest
|
|
1767
|
+
// `/api` and `/api/` on their own are left to the static host: a site's own
|
|
1768
|
+
// application can answer that route (this framework's server interface does),
|
|
1769
|
+
// and the worker has no business putting an API answer in front of it. Every
|
|
1770
|
+
// other `/api/...` is an API call on the short spelling, which routeRequest
|
|
1771
|
+
// canonicalises to `/SASjsApi`.
|
|
1771
1772
|
if (/^\/api\/?$/i.test(path)) return
|
|
1772
1773
|
|
|
1773
1774
|
if (!/^\/(SASjsApi|SASLogon|AppStream|api|app)(\/|$)/i.test(path)) return
|
|
@@ -14,6 +14,8 @@ export interface MockSiteResult {
|
|
|
14
14
|
* large folder or file. Zero when the site ships no snapshot.
|
|
15
15
|
*/
|
|
16
16
|
snapshotShards: number;
|
|
17
|
+
/** True when the site carries the API console at <base>/api-console/. */
|
|
18
|
+
apiConsole: boolean;
|
|
17
19
|
/** True when a recorded SAS session was shipped, for .sas runs to answer. */
|
|
18
20
|
sasTranscript: boolean;
|
|
19
21
|
/**
|
|
@@ -1008,6 +1008,75 @@ var patchIndexHtml = function (html, version, basePath, outputFolder) {
|
|
|
1008
1008
|
}
|
|
1009
1009
|
return { patched: patched, warnings: warnings };
|
|
1010
1010
|
};
|
|
1011
|
+
/**
|
|
1012
|
+
* The folder the console lives in, under the site root.
|
|
1013
|
+
*
|
|
1014
|
+
* Not `api`: an application can answer `/api` itself - this framework's own server
|
|
1015
|
+
* interface does - and a page there would collide with its route, so a site
|
|
1016
|
+
* refresh on that screen would land on the console instead of the application.
|
|
1017
|
+
*/
|
|
1018
|
+
var API_CONSOLE_DIR = 'api-console';
|
|
1019
|
+
/** The files the console consists of, under `<output>/api-console`. */
|
|
1020
|
+
var API_CONSOLE_FILES = [
|
|
1021
|
+
'index.html',
|
|
1022
|
+
'console.js',
|
|
1023
|
+
'console.css',
|
|
1024
|
+
'operations.json'
|
|
1025
|
+
];
|
|
1026
|
+
/**
|
|
1027
|
+
* The API console: a page at `<base>/api-console/` that lists every operation the API
|
|
1028
|
+
* document declares and sends them against this host.
|
|
1029
|
+
*
|
|
1030
|
+
* The route table is the same one the worker answers from (`ROUTES` from
|
|
1031
|
+
* @sasjs/api), so the console cannot drift from the API: a route the package
|
|
1032
|
+
* adds appears here on the next build, with the parameters it declares.
|
|
1033
|
+
*
|
|
1034
|
+
* Three files and the data, all static. It is a developer surface rather than a
|
|
1035
|
+
* screen of the app, so nothing in the page links to it and nothing preloads it -
|
|
1036
|
+
* a visit costs the demo nothing until someone types the URL.
|
|
1037
|
+
*/
|
|
1038
|
+
var writeApiConsole = function (outputFolder, version) {
|
|
1039
|
+
var folder = path_1.default.join(outputFolder, API_CONSOLE_DIR);
|
|
1040
|
+
// `<base>/api-console/` is this build's, not the project's: a site that ships its own
|
|
1041
|
+
// files there would have them replaced without a word, so it keeps them and
|
|
1042
|
+
// the console is left out
|
|
1043
|
+
var taken = API_CONSOLE_FILES.filter(function (file) {
|
|
1044
|
+
return fs_1.default.existsSync(path_1.default.join(folder, file));
|
|
1045
|
+
});
|
|
1046
|
+
if (taken.length)
|
|
1047
|
+
return {
|
|
1048
|
+
written: false,
|
|
1049
|
+
operations: 0,
|
|
1050
|
+
warning: "The site already carries ".concat(taken
|
|
1051
|
+
.map(function (file) { return API_CONSOLE_DIR + '/' + file; })
|
|
1052
|
+
.join(', '), ", so the API console was not written. ") +
|
|
1053
|
+
'Move those files, or set streamConfig.mockApi to false to silence this.'
|
|
1054
|
+
};
|
|
1055
|
+
fs_1.default.mkdirSync(folder, { recursive: true });
|
|
1056
|
+
// The console starts the host itself, so it has to ask for the same worker the
|
|
1057
|
+
// app's loader asks for: a registration is identified by its script URL, and a
|
|
1058
|
+
// bare `sw.js` here would replace the versioned one on every visit to either
|
|
1059
|
+
// page - an install nobody asked for, which is what the version exists to avoid.
|
|
1060
|
+
fs_1.default.writeFileSync(path_1.default.join(folder, 'index.html'), fs_1.default
|
|
1061
|
+
.readFileSync(path_1.default.join(ASSETS_FOLDER, 'api-index.html'), 'utf8')
|
|
1062
|
+
.replace('__MOCK_VERSION__', version));
|
|
1063
|
+
fs_1.default.copyFileSync(path_1.default.join(ASSETS_FOLDER, 'api-console.js'), path_1.default.join(folder, 'console.js'));
|
|
1064
|
+
fs_1.default.copyFileSync(path_1.default.join(ASSETS_FOLDER, 'api-console.css'), path_1.default.join(folder, 'console.css'));
|
|
1065
|
+
var operations = api_1.ROUTES.map(function (route) { return ({
|
|
1066
|
+
id: route.id,
|
|
1067
|
+
method: route.method,
|
|
1068
|
+
path: route.path,
|
|
1069
|
+
tag: route.tag,
|
|
1070
|
+
summary: route.summary,
|
|
1071
|
+
params: route.params,
|
|
1072
|
+
requestBodies: route.requestBodies,
|
|
1073
|
+
responses: route.responses,
|
|
1074
|
+
implemented: route.implemented,
|
|
1075
|
+
reason: route.reason
|
|
1076
|
+
}); });
|
|
1077
|
+
fs_1.default.writeFileSync(path_1.default.join(folder, 'operations.json'), JSON.stringify(operations, null, 2));
|
|
1078
|
+
return { written: true, operations: operations.length };
|
|
1079
|
+
};
|
|
1011
1080
|
/**
|
|
1012
1081
|
* Builds a static site that runs the project's JS services (its mocks) in the
|
|
1013
1082
|
* browser, so a frontend can be tested with no server and no SAS.
|
|
@@ -1024,11 +1093,11 @@ var patchIndexHtml = function (html, version, basePath, outputFolder) {
|
|
|
1024
1093
|
*/
|
|
1025
1094
|
function buildMockSite(target) {
|
|
1026
1095
|
return __awaiter(this, void 0, void 0, function () {
|
|
1027
|
-
var projectDir, appLocRaw, appLoc, buildFolder, basePath, webSourceRaw, webFolder, outputFolder, streamConfig, excluded, _a, seed, skipped, jsPrograms, sasPrograms, driveEntries, identity, webFileCount, hostFolder, _b, _c, asset, hostVersion, buildStamp, swPath, snapshotSource, hasSnapshot, snapshotShards, capture, files, split, _d, _e, shard, transcriptSource, hasTranscript, indexPath, _f, patched, warnings;
|
|
1096
|
+
var projectDir, appLocRaw, appLoc, buildFolder, basePath, webSourceRaw, webFolder, outputFolder, streamConfig, excluded, _a, seed, skipped, jsPrograms, sasPrograms, driveEntries, identity, webFileCount, hostFolder, _b, _c, asset, hostVersion, buildStamp, swPath, snapshotSource, hasSnapshot, snapshotShards, capture, files, split, _d, _e, shard, transcriptSource, hasTranscript, apiConsole, written, indexPath, _f, patched, warnings;
|
|
1028
1097
|
var e_11, _g, e_12, _h;
|
|
1029
|
-
var _j, _k, _l, _m, _o, _p;
|
|
1030
|
-
return __generator(this, function (
|
|
1031
|
-
switch (
|
|
1098
|
+
var _j, _k, _l, _m, _o, _p, _q, _r;
|
|
1099
|
+
return __generator(this, function (_s) {
|
|
1100
|
+
switch (_s.label) {
|
|
1032
1101
|
case 0:
|
|
1033
1102
|
projectDir = process.projectDir || process.cwd();
|
|
1034
1103
|
appLocRaw = target.appLoc;
|
|
@@ -1040,7 +1109,7 @@ function buildMockSite(target) {
|
|
|
1040
1109
|
path_1.default.join(projectDir, 'sasjsbuild');
|
|
1041
1110
|
return [4 /*yield*/, (0, utils_1.folderExists)(buildFolder)];
|
|
1042
1111
|
case 1:
|
|
1043
|
-
if (!(
|
|
1112
|
+
if (!(_s.sent())) {
|
|
1044
1113
|
throw new Error("Build folder not found: ".concat(buildFolder, ". Run `sasjs compile` first."));
|
|
1045
1114
|
}
|
|
1046
1115
|
basePath = normaliseBasePath((_j = target.streamConfig) === null || _j === void 0 ? void 0 : _j.mockWebBasePath);
|
|
@@ -1051,7 +1120,7 @@ function buildMockSite(target) {
|
|
|
1051
1120
|
webFolder = path_1.default.resolve(projectDir, webSourceRaw);
|
|
1052
1121
|
return [4 /*yield*/, (0, utils_1.folderExists)(webFolder)];
|
|
1053
1122
|
case 2:
|
|
1054
|
-
if (!(
|
|
1123
|
+
if (!(_s.sent())) {
|
|
1055
1124
|
throw new Error("Web source folder not found: ".concat(webFolder, ". Check streamConfig.webSourcePath - build the frontend first."));
|
|
1056
1125
|
}
|
|
1057
1126
|
if (!fs_1.default.existsSync(path_1.default.join(webFolder, 'index.html'))) {
|
|
@@ -1146,7 +1215,7 @@ function buildMockSite(target) {
|
|
|
1146
1215
|
case 3:
|
|
1147
1216
|
// 2. the linter, bundled from the installed packages so the code routes answer
|
|
1148
1217
|
// with the rules this CLI resolved rather than a second implementation
|
|
1149
|
-
|
|
1218
|
+
_s.sent();
|
|
1150
1219
|
// sw.js sits at the site root: a worker can only control the folder it is
|
|
1151
1220
|
// served from, and the app has to be controlled from /
|
|
1152
1221
|
fs_1.default.copyFileSync(path_1.default.join(ASSETS_FOLDER, 'sw.js'), path_1.default.join(outputFolder, 'sw.js'));
|
|
@@ -1250,6 +1319,13 @@ function buildMockSite(target) {
|
|
|
1250
1319
|
buildStamp: buildStamp,
|
|
1251
1320
|
builtAt: new Date().toISOString()
|
|
1252
1321
|
}, null, 2));
|
|
1322
|
+
apiConsole = false;
|
|
1323
|
+
if (((_q = target.streamConfig) === null || _q === void 0 ? void 0 : _q.mockApi) !== false) {
|
|
1324
|
+
written = writeApiConsole(outputFolder, hostVersion);
|
|
1325
|
+
apiConsole = written.written;
|
|
1326
|
+
if (written.warning)
|
|
1327
|
+
(_r = process.logger) === null || _r === void 0 ? void 0 : _r.warn(written.warning);
|
|
1328
|
+
}
|
|
1253
1329
|
indexPath = path_1.default.join(outputFolder, 'index.html');
|
|
1254
1330
|
_f = patchIndexHtml(fs_1.default.readFileSync(indexPath, 'utf8'), hostVersion, basePath, outputFolder), patched = _f.patched, warnings = _f.warnings;
|
|
1255
1331
|
fs_1.default.writeFileSync(indexPath, patched);
|
|
@@ -1262,6 +1338,7 @@ function buildMockSite(target) {
|
|
|
1262
1338
|
webFileCount: webFileCount,
|
|
1263
1339
|
snapshot: hasSnapshot,
|
|
1264
1340
|
snapshotShards: snapshotShards,
|
|
1341
|
+
apiConsole: apiConsole,
|
|
1265
1342
|
sasTranscript: hasTranscript,
|
|
1266
1343
|
identitySource: identity.source
|
|
1267
1344
|
}];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|