@ms-cloudpack/app-server 0.1.1
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 +7 -0
- package/lib/cookieNames.d.ts +8 -0
- package/lib/cookieNames.d.ts.map +1 -0
- package/lib/cookieNames.js +9 -0
- package/lib/cookieNames.js.map +1 -0
- package/lib/createRoutes.d.ts +23 -0
- package/lib/createRoutes.d.ts.map +1 -0
- package/lib/createRoutes.js +75 -0
- package/lib/createRoutes.js.map +1 -0
- package/lib/exitIfPortUnavailable.d.ts +5 -0
- package/lib/exitIfPortUnavailable.d.ts.map +1 -0
- package/lib/exitIfPortUnavailable.js +14 -0
- package/lib/exitIfPortUnavailable.js.map +1 -0
- package/lib/getDefaultHtmlResponse.d.ts +6 -0
- package/lib/getDefaultHtmlResponse.d.ts.map +1 -0
- package/lib/getDefaultHtmlResponse.js +18 -0
- package/lib/getDefaultHtmlResponse.js.map +1 -0
- package/lib/getHtmlResponse.d.ts +6 -0
- package/lib/getHtmlResponse.d.ts.map +1 -0
- package/lib/getHtmlResponse.js +117 -0
- package/lib/getHtmlResponse.js.map +1 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -0
- package/lib/inlineScripts/defineProcess.inline.d.ts +1 -0
- package/lib/inlineScripts/defineProcess.inline.d.ts.map +1 -0
- package/lib/inlineScripts/defineProcess.inline.js +8 -0
- package/lib/inlineScripts/defineProcess.inline.js.map +1 -0
- package/lib/inlineScripts/errorHandler.inline.d.ts +8 -0
- package/lib/inlineScripts/errorHandler.inline.d.ts.map +1 -0
- package/lib/inlineScripts/errorHandler.inline.js +19 -0
- package/lib/inlineScripts/errorHandler.inline.js.map +1 -0
- package/lib/inlineScripts/getBrowserCacheRatio.inline.d.ts +2 -0
- package/lib/inlineScripts/getBrowserCacheRatio.inline.d.ts.map +1 -0
- package/lib/inlineScripts/getBrowserCacheRatio.inline.js +43 -0
- package/lib/inlineScripts/getBrowserCacheRatio.inline.js.map +1 -0
- package/lib/inlineScripts/getInlineScripts.d.ts +2 -0
- package/lib/inlineScripts/getInlineScripts.d.ts.map +1 -0
- package/lib/inlineScripts/getInlineScripts.js +15 -0
- package/lib/inlineScripts/getInlineScripts.js.map +1 -0
- package/lib/inlineScripts/getPageLoadTime.inline.d.ts +2 -0
- package/lib/inlineScripts/getPageLoadTime.inline.d.ts.map +1 -0
- package/lib/inlineScripts/getPageLoadTime.inline.js +64 -0
- package/lib/inlineScripts/getPageLoadTime.inline.js.map +1 -0
- package/lib/setHeaders.d.ts +13 -0
- package/lib/setHeaders.d.ts.map +1 -0
- package/lib/setHeaders.js +15 -0
- package/lib/setHeaders.js.map +1 -0
- package/lib/startAppServer.d.ts +28 -0
- package/lib/startAppServer.d.ts.map +1 -0
- package/lib/startAppServer.js +55 -0
- package/lib/startAppServer.js.map +1 -0
- package/lib/tsdoc-metadata.json +11 -0
- package/lib/types/AppServer.d.ts +6 -0
- package/lib/types/AppServer.d.ts.map +1 -0
- package/lib/types/AppServer.js +2 -0
- package/lib/types/AppServer.js.map +1 -0
- package/lib/utilities/dynamicImportExtension.d.ts +6 -0
- package/lib/utilities/dynamicImportExtension.d.ts.map +1 -0
- package/lib/utilities/dynamicImportExtension.js +8 -0
- package/lib/utilities/dynamicImportExtension.js.map +1 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# @ms-cloudpack/app-server
|
|
2
|
+
|
|
3
|
+
This package provides an App server for Cloudpack.
|
|
4
|
+
|
|
5
|
+
The app server is an express server which hosts the appropriate routes for the web app, primarily returning html content which loads resources from the bundle server. The app server can support whichever routes the app needs and also serve static paths.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const cookieNames: {
|
|
2
|
+
readonly bundleServerUrl: "cloudpack-bundle-server-url";
|
|
3
|
+
readonly sessionId: string;
|
|
4
|
+
readonly sessionSequence: string;
|
|
5
|
+
readonly apiUrl: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function getCloudpackSessionVersionCookie(projectName: string): string;
|
|
8
|
+
//# sourceMappingURL=cookieNames.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookieNames.d.ts","sourceRoot":"","sources":["../src/cookieNames.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW;;;;;CAGd,CAAC;AAEX,wBAAgB,gCAAgC,CAAC,WAAW,EAAE,MAAM,UAEnE"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { cookieNames as overlayCookieNames } from '@ms-cloudpack/overlay/constants';
|
|
2
|
+
export const cookieNames = {
|
|
3
|
+
...overlayCookieNames,
|
|
4
|
+
bundleServerUrl: 'cloudpack-bundle-server-url',
|
|
5
|
+
};
|
|
6
|
+
export function getCloudpackSessionVersionCookie(projectName) {
|
|
7
|
+
return 'cloudpack-sessionVersion-' + projectName;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=cookieNames.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookieNames.js","sourceRoot":"","sources":["../src/cookieNames.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,IAAI,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAEpF,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,GAAG,kBAAkB;IACrB,eAAe,EAAE,6BAA6B;CACtC,CAAC;AAEX,MAAM,UAAU,gCAAgC,CAAC,WAAmB;IAClE,OAAO,2BAA2B,GAAG,WAAW,CAAC;AACnD,CAAC","sourcesContent":["import { cookieNames as overlayCookieNames } from '@ms-cloudpack/overlay/constants';\n\nexport const cookieNames = {\n ...overlayCookieNames,\n bundleServerUrl: 'cloudpack-bundle-server-url',\n} as const;\n\nexport function getCloudpackSessionVersionCookie(projectName: string) {\n return 'cloudpack-sessionVersion-' + projectName;\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { PackageDefinitionsCache } from '@ms-cloudpack/bundler-types';
|
|
2
|
+
import type { Express } from '@ms-cloudpack/create-express-app';
|
|
3
|
+
import type { BundleServer } from '@ms-cloudpack/bundle-server';
|
|
4
|
+
import type { ApiServer, Session } from '@ms-cloudpack/api-server';
|
|
5
|
+
import type { CloudpackConfig, PackageJson } from '@ms-cloudpack/config-types';
|
|
6
|
+
import type { PackageImportPaths } from '@ms-cloudpack/package-utilities';
|
|
7
|
+
import type { PackageHashes } from '@ms-cloudpack/package-hashes';
|
|
8
|
+
/**
|
|
9
|
+
* Creates the routes for the express app, considering how the config is defined.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createRoutes({ app, url, session, definition, bundleServer, apiServer, config, packages, packageImportPaths, packageHashes, }: {
|
|
12
|
+
app: Express;
|
|
13
|
+
url: string;
|
|
14
|
+
session: Session;
|
|
15
|
+
definition: PackageJson;
|
|
16
|
+
bundleServer: BundleServer;
|
|
17
|
+
apiServer: ApiServer;
|
|
18
|
+
config: CloudpackConfig;
|
|
19
|
+
packages: PackageDefinitionsCache;
|
|
20
|
+
packageImportPaths: PackageImportPaths;
|
|
21
|
+
packageHashes: PackageHashes;
|
|
22
|
+
}): void;
|
|
23
|
+
//# sourceMappingURL=createRoutes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createRoutes.d.ts","sourceRoot":"","sources":["../src/createRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAC3E,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,kCAAkC,CAAC;AAKnF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAInE,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC/E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE;;GAEG;AACH,wBAAgB,YAAY,CAAC,EAC3B,GAAG,EACH,GAAG,EACH,OAAO,EACP,UAAU,EACV,YAAY,EACZ,SAAS,EACT,MAAM,EACN,QAAQ,EACR,kBAAkB,EAClB,aAAa,GACd,EAAE;IACD,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,WAAW,CAAC;IACxB,YAAY,EAAE,YAAY,CAAC;IAC3B,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,EAAE,eAAe,CAAC;IACxB,QAAQ,EAAE,uBAAuB,CAAC;IAClC,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,aAAa,EAAE,aAAa,CAAC;CAC9B,QA0EA"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { express } from '@ms-cloudpack/create-express-app';
|
|
2
|
+
import { createImportMap } from '@ms-cloudpack/package-utilities';
|
|
3
|
+
import { slash } from '@ms-cloudpack/path-string-parsing';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { getHtmlResponse } from './getHtmlResponse.js';
|
|
6
|
+
import { getInlineScripts } from './inlineScripts/getInlineScripts.js';
|
|
7
|
+
import { setHeaders } from './setHeaders.js';
|
|
8
|
+
/**
|
|
9
|
+
* Creates the routes for the express app, considering how the config is defined.
|
|
10
|
+
*/
|
|
11
|
+
export function createRoutes({ app, url, session, definition, bundleServer, apiServer, config, packages, packageImportPaths, packageHashes, }) {
|
|
12
|
+
const routes = [...(config.devServer?.routes || [])];
|
|
13
|
+
const hasDefaultRoute = routes.some((route) => route.match === '*' || route.match === '/');
|
|
14
|
+
if (!hasDefaultRoute) {
|
|
15
|
+
routes.push({
|
|
16
|
+
match: '*',
|
|
17
|
+
exportEntry: '.',
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
// Hashes from external packages to be used in the import map.
|
|
21
|
+
const getPackageHash = (packagePath) => {
|
|
22
|
+
return packageHashes.get({ packagePath, isSourceHashingEnabled: false });
|
|
23
|
+
};
|
|
24
|
+
for (const route of routes) {
|
|
25
|
+
if (route.staticPath) {
|
|
26
|
+
app.use(route.match, express.static(path.resolve(session.appPath, route.staticPath)));
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
app.get(route.match, (req, res) => {
|
|
30
|
+
(async () => {
|
|
31
|
+
// Build the import map if it hasn't been built yet for this session version.
|
|
32
|
+
const importMap = session.getImportMap() ||
|
|
33
|
+
session.setImportMap(await createImportMap({
|
|
34
|
+
resolveMap: session.resolveMap,
|
|
35
|
+
bundleServerUrl: bundleServer.url,
|
|
36
|
+
sessionVersion: session.getSessionVersion(),
|
|
37
|
+
targetVersions: session.targetVersions,
|
|
38
|
+
hashPackages: config.features?.hashPackages,
|
|
39
|
+
}, { packages, packageImportPaths, getPackageHash, config: session.config }));
|
|
40
|
+
// Parse the request path, extension, grab the overlay script path.
|
|
41
|
+
const requestPath = slash(req.path.substring(1));
|
|
42
|
+
const requestExt = path.extname(requestPath);
|
|
43
|
+
const overlayScript = importMap.imports['@ms-cloudpack/overlay'];
|
|
44
|
+
const entryScript =
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
46
|
+
route.exportEntry && importMap.imports[slash(path.join(definition.name, route.exportEntry))];
|
|
47
|
+
// Set the appropriate Cloudpack headers/cookies in the response.
|
|
48
|
+
// TODO: setting the headers here prohibits cases where the page rendering is owned by existing server code
|
|
49
|
+
// that can only accept changing the scripts. We should consider moving to a model where an initial script
|
|
50
|
+
// fetch loads the import map and sets Cloudpack settings in local storage rather than headers/cookies.
|
|
51
|
+
setHeaders({ res, session, apiServer, bundleServer });
|
|
52
|
+
const inlineScripts = await getInlineScripts();
|
|
53
|
+
const { content, statusCode, contentType } = await getHtmlResponse({
|
|
54
|
+
route,
|
|
55
|
+
baseUrl: url,
|
|
56
|
+
importMap,
|
|
57
|
+
overlayScript,
|
|
58
|
+
entryScript,
|
|
59
|
+
session,
|
|
60
|
+
definition,
|
|
61
|
+
inlineScripts,
|
|
62
|
+
req,
|
|
63
|
+
res,
|
|
64
|
+
});
|
|
65
|
+
res.type(contentType).status(statusCode).send(content).end();
|
|
66
|
+
console.debug(`App server: Request: ${requestPath}, ext: ${requestExt}`);
|
|
67
|
+
})().catch((err) => {
|
|
68
|
+
console.error(err?.stack || err);
|
|
69
|
+
res.status(500).send(`Error loading app: ${err}`);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=createRoutes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createRoutes.js","sourceRoot":"","sources":["../src/createRoutes.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PackageDefinitionsCache } from '@ms-cloudpack/bundler-types';\nimport type { Express, Request, Response } from '@ms-cloudpack/create-express-app';\nimport { express } from '@ms-cloudpack/create-express-app';\nimport { createImportMap } from '@ms-cloudpack/package-utilities';\nimport { slash } from '@ms-cloudpack/path-string-parsing';\nimport path from 'path';\nimport type { BundleServer } from '@ms-cloudpack/bundle-server';\nimport type { ApiServer, Session } from '@ms-cloudpack/api-server';\nimport { getHtmlResponse } from './getHtmlResponse.js';\nimport { getInlineScripts } from './inlineScripts/getInlineScripts.js';\nimport { setHeaders } from './setHeaders.js';\nimport type { CloudpackConfig, PackageJson } from '@ms-cloudpack/config-types';\nimport type { PackageImportPaths } from '@ms-cloudpack/package-utilities';\nimport type { PackageHashes } from '@ms-cloudpack/package-hashes';\n\n/**\n * Creates the routes for the express app, considering how the config is defined.\n */\nexport function createRoutes({\n app,\n url,\n session,\n definition,\n bundleServer,\n apiServer,\n config,\n packages,\n packageImportPaths,\n packageHashes,\n}: {\n app: Express;\n url: string;\n session: Session;\n definition: PackageJson;\n bundleServer: BundleServer;\n apiServer: ApiServer;\n config: CloudpackConfig;\n packages: PackageDefinitionsCache;\n packageImportPaths: PackageImportPaths;\n packageHashes: PackageHashes;\n}) {\n const routes = [...(config.devServer?.routes || [])];\n const hasDefaultRoute = routes.some((route) => route.match === '*' || route.match === '/');\n\n if (!hasDefaultRoute) {\n routes.push({\n match: '*',\n exportEntry: '.',\n });\n }\n\n // Hashes from external packages to be used in the import map.\n const getPackageHash = (packagePath: string) => {\n return packageHashes.get({ packagePath, isSourceHashingEnabled: false });\n };\n\n for (const route of routes) {\n if (route.staticPath) {\n app.use(route.match, express.static(path.resolve(session.appPath, route.staticPath)));\n } else {\n app.get(route.match, (req: Request, res: Response) => {\n (async () => {\n // Build the import map if it hasn't been built yet for this session version.\n const importMap =\n session.getImportMap() ||\n session.setImportMap(\n await createImportMap(\n {\n resolveMap: session.resolveMap,\n bundleServerUrl: bundleServer.url,\n sessionVersion: session.getSessionVersion(),\n targetVersions: session.targetVersions,\n hashPackages: config.features?.hashPackages,\n },\n { packages, packageImportPaths, getPackageHash, config: session.config },\n ),\n );\n\n // Parse the request path, extension, grab the overlay script path.\n const requestPath = slash(req.path.substring(1));\n const requestExt = path.extname(requestPath);\n const overlayScript = importMap.imports['@ms-cloudpack/overlay'];\n const entryScript =\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n route.exportEntry && importMap.imports[slash(path.join(definition.name!, route.exportEntry))];\n // Set the appropriate Cloudpack headers/cookies in the response.\n // TODO: setting the headers here prohibits cases where the page rendering is owned by existing server code\n // that can only accept changing the scripts. We should consider moving to a model where an initial script\n // fetch loads the import map and sets Cloudpack settings in local storage rather than headers/cookies.\n setHeaders({ res, session, apiServer, bundleServer });\n const inlineScripts = await getInlineScripts();\n const { content, statusCode, contentType } = await getHtmlResponse({\n route,\n baseUrl: url,\n importMap,\n overlayScript,\n entryScript,\n session,\n definition,\n inlineScripts,\n req,\n res,\n });\n\n res.type(contentType).status(statusCode).send(content).end();\n\n console.debug(`App server: Request: ${requestPath}, ext: ${requestExt}`);\n })().catch((err) => {\n console.error((err as Error)?.stack || err);\n res.status(500).send(`Error loading app: ${err}`);\n });\n });\n }\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exitIfPortUnavailable.d.ts","sourceRoot":"","sources":["../src/exitIfPortUnavailable.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,QAehG"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Report the error and exit if the port is already in use.
|
|
3
|
+
*/
|
|
4
|
+
export function exitIfPortUnavailable(err, projectName, ports) {
|
|
5
|
+
const portRange = typeof ports === 'number' ? [ports] : ports;
|
|
6
|
+
if (err instanceof Error && err.message === 'Specified port not available') {
|
|
7
|
+
const portNeedsPrivileges = portRange.some((port) => port < 1024)
|
|
8
|
+
? '\nNote: for Mac/Linux/WSL users, you may need to run Cloudpack with elevated privileges to use port numbers less than 1024. Try running `sudo cloudpack start`.'
|
|
9
|
+
: '';
|
|
10
|
+
console.error(`Cloudpack could not start "${projectName}" because no port from the allowed list was available: ${portRange.join(', ')}. Close other programs and try again.`, portNeedsPrivileges);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=exitIfPortUnavailable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exitIfPortUnavailable.js","sourceRoot":"","sources":["../src/exitIfPortUnavailable.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAY,EAAE,WAAmB,EAAE,KAAwB;IAC/F,MAAM,SAAS,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC9D,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,OAAO,KAAK,8BAA8B,EAAE,CAAC;QAC3E,MAAM,mBAAmB,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,IAAI,CAAC;YAC/D,CAAC,CAAC,iKAAiK;YACnK,CAAC,CAAC,EAAE,CAAC;QACP,OAAO,CAAC,KAAK,CACX,8BAA8B,WAAW,0DAA0D,SAAS,CAAC,IAAI,CAC/G,IAAI,CACL,uCAAuC,EACxC,mBAAmB,CACpB,CAAC;QAEF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC","sourcesContent":["/**\n * Report the error and exit if the port is already in use.\n */\nexport function exitIfPortUnavailable(err: unknown, projectName: string, ports: number | number[]) {\n const portRange = typeof ports === 'number' ? [ports] : ports;\n if (err instanceof Error && err.message === 'Specified port not available') {\n const portNeedsPrivileges = portRange.some((port) => port < 1024)\n ? '\\nNote: for Mac/Linux/WSL users, you may need to run Cloudpack with elevated privileges to use port numbers less than 1024. Try running `sudo cloudpack start`.'\n : '';\n console.error(\n `Cloudpack could not start \"${projectName}\" because no port from the allowed list was available: ${portRange.join(\n ', ',\n )}. Close other programs and try again.`,\n portNeedsPrivileges,\n );\n\n process.exit(1);\n }\n}\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CreateHtmlResult, CreateHtmlOptions } from '@ms-cloudpack/api-server';
|
|
2
|
+
/**
|
|
3
|
+
* Return the default HTML response.
|
|
4
|
+
*/
|
|
5
|
+
export declare function getDefaultHtmlResponse({ definition }: CreateHtmlOptions): CreateHtmlResult;
|
|
6
|
+
//# sourceMappingURL=getDefaultHtmlResponse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getDefaultHtmlResponse.d.ts","sourceRoot":"","sources":["../src/getDefaultHtmlResponse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAEpF;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,UAAU,EAAE,EAAE,iBAAiB,GAAG,gBAAgB,CAc1F"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return the default HTML response.
|
|
3
|
+
*/
|
|
4
|
+
export function getDefaultHtmlResponse({ definition }) {
|
|
5
|
+
const { name } = definition;
|
|
6
|
+
return [
|
|
7
|
+
`<!DOCTYPE html>`,
|
|
8
|
+
`<html lang="en">`,
|
|
9
|
+
`<head>`,
|
|
10
|
+
` <title>${name}</title>`,
|
|
11
|
+
`</head>`,
|
|
12
|
+
`<body>`,
|
|
13
|
+
`<div id="root"></div>`,
|
|
14
|
+
`</body>`,
|
|
15
|
+
`</html>`,
|
|
16
|
+
].join('\n');
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=getDefaultHtmlResponse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getDefaultHtmlResponse.js","sourceRoot":"","sources":["../src/getDefaultHtmlResponse.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,EAAE,UAAU,EAAqB;IACtE,MAAM,EAAE,IAAI,EAAE,GAAG,UAAU,CAAC;IAE5B,OAAO;QACL,iBAAiB;QACjB,kBAAkB;QAClB,QAAQ;QACR,YAAY,IAAI,UAAU;QAC1B,SAAS;QACT,QAAQ;QACR,uBAAuB;QACvB,SAAS;QACT,SAAS;KACV,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC","sourcesContent":["import type { CreateHtmlResult, CreateHtmlOptions } from '@ms-cloudpack/api-server';\n\n/**\n * Return the default HTML response.\n */\nexport function getDefaultHtmlResponse({ definition }: CreateHtmlOptions): CreateHtmlResult {\n const { name } = definition;\n\n return [\n `<!DOCTYPE html>`,\n `<html lang=\"en\">`,\n `<head>`,\n ` <title>${name}</title>`,\n `</head>`,\n `<body>`,\n `<div id=\"root\"></div>`,\n `</body>`,\n `</html>`,\n ].join('\\n');\n}\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CreateHtmlResult, CreateHtmlOptions } from '@ms-cloudpack/api-server';
|
|
2
|
+
/**
|
|
3
|
+
* Get the HTML response for the given route. If the route has a custom render script, use that.
|
|
4
|
+
*/
|
|
5
|
+
export declare function getHtmlResponse(options: CreateHtmlOptions): Promise<Required<Exclude<CreateHtmlResult, string>>>;
|
|
6
|
+
//# sourceMappingURL=getHtmlResponse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getHtmlResponse.d.ts","sourceRoot":"","sources":["../src/getHtmlResponse.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAGV,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,0BAA0B,CAAC;AAKlC;;GAEG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAAC,CAwFtD"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { pathToFileURL } from 'url';
|
|
3
|
+
import { getDefaultHtmlResponse } from './getDefaultHtmlResponse.js';
|
|
4
|
+
import { JSDOM } from 'jsdom';
|
|
5
|
+
import fsPromises from 'fs/promises';
|
|
6
|
+
/**
|
|
7
|
+
* Get the HTML response for the given route. If the route has a custom render script, use that.
|
|
8
|
+
*/
|
|
9
|
+
export async function getHtmlResponse(options) {
|
|
10
|
+
const { route, session, overlayScript, entryScript, inlineScripts } = options;
|
|
11
|
+
let createHtml = getDefaultHtmlResponse;
|
|
12
|
+
if (route.renderScript) {
|
|
13
|
+
const renderScriptPath = path.resolve(session.appPath, route.renderScript);
|
|
14
|
+
let cacheBreakerQueryParam = `?t=${Date.now()}`;
|
|
15
|
+
try {
|
|
16
|
+
const { mtime } = await fsPromises.stat(path.resolve(session.appPath, route.renderScript));
|
|
17
|
+
cacheBreakerQueryParam = `?t=${mtime.getTime()}`;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
/* no-op */
|
|
21
|
+
}
|
|
22
|
+
if (isHtml(renderScriptPath)) {
|
|
23
|
+
createHtml = readStaticHtml;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
// Get the html factory function from a script default export.
|
|
27
|
+
const renderScriptUrl = pathToFileURL(renderScriptPath).toString();
|
|
28
|
+
// Note: because there isn't a way to purge the require cache, we need to add a cache breaker query param to the
|
|
29
|
+
// script path to ensure we get the latest version of the script. This could be improved by using the git hash of
|
|
30
|
+
// the file if it exists, or a hash of the content, or even the timestamp of the file.
|
|
31
|
+
try {
|
|
32
|
+
createHtml = (await import(renderScriptUrl + cacheBreakerQueryParam)).default;
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
console.error(`Error importing render script at "${renderScriptUrl}":`, e);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (!createHtml) {
|
|
39
|
+
console.error(`The render script at "${renderScriptPath}" does not export a default function.`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
let content;
|
|
43
|
+
let contentType = 'html';
|
|
44
|
+
let statusCode = 200;
|
|
45
|
+
const result = await createHtml(options);
|
|
46
|
+
if (typeof result === 'string') {
|
|
47
|
+
content = result;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
content = result.content;
|
|
51
|
+
contentType = result.contentType ?? contentType;
|
|
52
|
+
statusCode = result.statusCode ?? statusCode;
|
|
53
|
+
}
|
|
54
|
+
// endsWith('html') covers: 'html', '.html', and 'text/html'
|
|
55
|
+
if (contentType.endsWith('html')) {
|
|
56
|
+
try {
|
|
57
|
+
const htmlDocument = new JSDOM(content);
|
|
58
|
+
const { document } = htmlDocument.window;
|
|
59
|
+
// inject the import map.
|
|
60
|
+
if (entryScript || overlayScript) {
|
|
61
|
+
const script = document.createElement('script');
|
|
62
|
+
script.type = 'importmap';
|
|
63
|
+
script.innerHTML = JSON.stringify(session.getImportMap(), null, 2);
|
|
64
|
+
document.head.prepend(script);
|
|
65
|
+
}
|
|
66
|
+
for (const inlineScript of inlineScripts) {
|
|
67
|
+
const script = document.createElement('script');
|
|
68
|
+
script.type = 'module';
|
|
69
|
+
script.innerHTML = inlineScript;
|
|
70
|
+
document.head.appendChild(script);
|
|
71
|
+
}
|
|
72
|
+
// inject the overlay script.
|
|
73
|
+
addScript(document.head, overlayScript);
|
|
74
|
+
// inject the entry script.
|
|
75
|
+
addScript(document.head, entryScript);
|
|
76
|
+
content = htmlDocument.serialize();
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
console.error(`Error parsing html response for rendering route "${route.match}"`, e);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
content,
|
|
84
|
+
statusCode,
|
|
85
|
+
contentType,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Helper function to add a script to the document.
|
|
90
|
+
*/
|
|
91
|
+
function addScript(parentElement, url) {
|
|
92
|
+
if (url) {
|
|
93
|
+
const script = parentElement.ownerDocument.createElement('script');
|
|
94
|
+
script.type = 'module';
|
|
95
|
+
script.src = url;
|
|
96
|
+
parentElement.appendChild(script);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function isHtml(scriptPath) {
|
|
100
|
+
// path has an .htm or .html extension; case insensitive.
|
|
101
|
+
return scriptPath.toLowerCase().match(/\.htm(l)?$/) !== null;
|
|
102
|
+
}
|
|
103
|
+
async function readStaticHtml(options) {
|
|
104
|
+
const { route } = options;
|
|
105
|
+
let html = '';
|
|
106
|
+
if (route.renderScript) {
|
|
107
|
+
const htmlPath = path.resolve(options.session.appPath, route.renderScript);
|
|
108
|
+
try {
|
|
109
|
+
html = (await fsPromises.readFile(htmlPath, 'utf8')) || '';
|
|
110
|
+
}
|
|
111
|
+
catch (e) {
|
|
112
|
+
console.error(`Error reading HTML file at "${htmlPath}".`, e);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return html;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=getHtmlResponse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getHtmlResponse.js","sourceRoot":"","sources":["../src/getHtmlResponse.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAOpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,UAAU,MAAM,aAAa,CAAC;AAErC;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,OAA0B;IAE1B,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC;IAC9E,IAAI,UAAU,GAAuB,sBAAsB,CAAC;IAE5D,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACvB,MAAM,gBAAgB,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;QAC3E,IAAI,sBAAsB,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QAEhD,IAAI,CAAC;YACH,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;YAE3F,sBAAsB,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QACnD,CAAC;QAAC,MAAM,CAAC;YACP,WAAW;QACb,CAAC;QAED,IAAI,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC7B,UAAU,GAAG,cAAc,CAAC;QAC9B,CAAC;aAAM,CAAC;YACN,8DAA8D;YAC9D,MAAM,eAAe,GAAG,aAAa,CAAC,gBAAgB,CAAC,CAAC,QAAQ,EAAE,CAAC;YAEnE,gHAAgH;YAChH,iHAAiH;YACjH,sFAAsF;YACtF,IAAI,CAAC;gBACH,UAAU,GAAI,CAAC,MAAM,MAAM,CAAC,eAAe,GAAG,sBAAsB,CAAC,CAAsB,CAAC,OAAO,CAAC;YACtG,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,qCAAqC,eAAe,IAAI,EAAE,CAAC,CAAC,CAAC;YAC7E,CAAC;QACH,CAAC;QAED,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,CAAC,KAAK,CAAC,yBAAyB,gBAAgB,uCAAuC,CAAC,CAAC;QAClG,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC;IACZ,IAAI,WAAW,GAAG,MAAM,CAAC;IACzB,IAAI,UAAU,GAAG,GAAG,CAAC;IAErB,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC;IACzC,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,GAAG,MAAM,CAAC;IACnB,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;QACzB,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,WAAW,CAAC;QAChD,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,UAAU,CAAC;IAC/C,CAAC;IAED,4DAA4D;IAC5D,IAAI,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;YACxC,MAAM,EAAE,QAAQ,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC;YAEzC,yBAAyB;YACzB,IAAI,WAAW,IAAI,aAAa,EAAE,CAAC;gBACjC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;gBAChD,MAAM,CAAC,IAAI,GAAG,WAAW,CAAC;gBAC1B,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;gBACnE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAChC,CAAC;YAED,KAAK,MAAM,YAAY,IAAI,aAAa,EAAE,CAAC;gBACzC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;gBAChD,MAAM,CAAC,IAAI,GAAG,QAAQ,CAAC;gBACvB,MAAM,CAAC,SAAS,GAAG,YAAY,CAAC;gBAChC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YACpC,CAAC;YAED,6BAA6B;YAC7B,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;YAExC,2BAA2B;YAC3B,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;YAEtC,OAAO,GAAG,YAAY,CAAC,SAAS,EAAE,CAAC;QACrC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,KAAK,CAAC,oDAAoD,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC;QACvF,CAAC;IACH,CAAC;IAED,OAAO;QACL,OAAO;QACP,UAAU;QACV,WAAW;KACZ,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,SAAS,CAAC,aAA0B,EAAE,GAAuB;IACpE,IAAI,GAAG,EAAE,CAAC;QACR,MAAM,MAAM,GAAG,aAAa,CAAC,aAAa,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAEnE,MAAM,CAAC,IAAI,GAAG,QAAQ,CAAC;QACvB,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC;QACjB,aAAa,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,UAAkB;IAChC,yDAAyD;IACzD,OAAO,UAAU,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;AAC/D,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,OAA0B;IACtD,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAC1B,IAAI,IAAI,GAAG,EAAE,CAAC;IAEd,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;QAE3E,IAAI,CAAC;YACH,IAAI,GAAG,CAAC,MAAM,UAAU,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7D,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,KAAK,CAAC,+BAA+B,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import path from 'path';\nimport { pathToFileURL } from 'url';\nimport type {\n CreateHtmlScript,\n CreateHtmlFunction,\n CreateHtmlResult,\n CreateHtmlOptions,\n} from '@ms-cloudpack/api-server';\nimport { getDefaultHtmlResponse } from './getDefaultHtmlResponse.js';\nimport { JSDOM } from 'jsdom';\nimport fsPromises from 'fs/promises';\n\n/**\n * Get the HTML response for the given route. If the route has a custom render script, use that.\n */\nexport async function getHtmlResponse(\n options: CreateHtmlOptions,\n): Promise<Required<Exclude<CreateHtmlResult, string>>> {\n const { route, session, overlayScript, entryScript, inlineScripts } = options;\n let createHtml: CreateHtmlFunction = getDefaultHtmlResponse;\n\n if (route.renderScript) {\n const renderScriptPath = path.resolve(session.appPath, route.renderScript);\n let cacheBreakerQueryParam = `?t=${Date.now()}`;\n\n try {\n const { mtime } = await fsPromises.stat(path.resolve(session.appPath, route.renderScript));\n\n cacheBreakerQueryParam = `?t=${mtime.getTime()}`;\n } catch {\n /* no-op */\n }\n\n if (isHtml(renderScriptPath)) {\n createHtml = readStaticHtml;\n } else {\n // Get the html factory function from a script default export.\n const renderScriptUrl = pathToFileURL(renderScriptPath).toString();\n\n // Note: because there isn't a way to purge the require cache, we need to add a cache breaker query param to the\n // script path to ensure we get the latest version of the script. This could be improved by using the git hash of\n // the file if it exists, or a hash of the content, or even the timestamp of the file.\n try {\n createHtml = ((await import(renderScriptUrl + cacheBreakerQueryParam)) as CreateHtmlScript).default;\n } catch (e) {\n console.error(`Error importing render script at \"${renderScriptUrl}\":`, e);\n }\n }\n\n if (!createHtml) {\n console.error(`The render script at \"${renderScriptPath}\" does not export a default function.`);\n }\n }\n\n let content;\n let contentType = 'html';\n let statusCode = 200;\n\n const result = await createHtml(options);\n if (typeof result === 'string') {\n content = result;\n } else {\n content = result.content;\n contentType = result.contentType ?? contentType;\n statusCode = result.statusCode ?? statusCode;\n }\n\n // endsWith('html') covers: 'html', '.html', and 'text/html'\n if (contentType.endsWith('html')) {\n try {\n const htmlDocument = new JSDOM(content);\n const { document } = htmlDocument.window;\n\n // inject the import map.\n if (entryScript || overlayScript) {\n const script = document.createElement('script');\n script.type = 'importmap';\n script.innerHTML = JSON.stringify(session.getImportMap(), null, 2);\n document.head.prepend(script);\n }\n\n for (const inlineScript of inlineScripts) {\n const script = document.createElement('script');\n script.type = 'module';\n script.innerHTML = inlineScript;\n document.head.appendChild(script);\n }\n\n // inject the overlay script.\n addScript(document.head, overlayScript);\n\n // inject the entry script.\n addScript(document.head, entryScript);\n\n content = htmlDocument.serialize();\n } catch (e) {\n console.error(`Error parsing html response for rendering route \"${route.match}\"`, e);\n }\n }\n\n return {\n content,\n statusCode,\n contentType,\n };\n}\n\n/**\n * Helper function to add a script to the document.\n */\nfunction addScript(parentElement: HTMLElement, url: string | undefined) {\n if (url) {\n const script = parentElement.ownerDocument.createElement('script');\n\n script.type = 'module';\n script.src = url;\n parentElement.appendChild(script);\n }\n}\n\nfunction isHtml(scriptPath: string): boolean {\n // path has an .htm or .html extension; case insensitive.\n return scriptPath.toLowerCase().match(/\\.htm(l)?$/) !== null;\n}\n\nasync function readStaticHtml(options: CreateHtmlOptions): Promise<string> {\n const { route } = options;\n let html = '';\n\n if (route.renderScript) {\n const htmlPath = path.resolve(options.session.appPath, route.renderScript);\n\n try {\n html = (await fsPromises.readFile(htmlPath, 'utf8')) || '';\n } catch (e) {\n console.error(`Error reading HTML file at \"${htmlPath}\".`, e);\n }\n }\n\n return html;\n}\n"]}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC"}
|
package/lib/index.js
ADDED
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC","sourcesContent":["export { startAppServer } from './startAppServer.js';\nexport type { AppServer } from './types/AppServer.js';\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=defineProcess.inline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defineProcess.inline.d.ts","sourceRoot":"","sources":["../../src/inlineScripts/defineProcess.inline.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// This file must NOT import other files, because they won't be resolved at runtime.
|
|
3
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4
|
+
window.process ??= {};
|
|
5
|
+
window.process.env ??= {};
|
|
6
|
+
// Assign true to process.browser for packages like readable-stream
|
|
7
|
+
process.browser = true;
|
|
8
|
+
//# sourceMappingURL=defineProcess.inline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defineProcess.inline.js","sourceRoot":"","sources":["../../src/inlineScripts/defineProcess.inline.ts"],"names":[],"mappings":";AAAA,oFAAoF;AAEpF,8DAA8D;AAC9D,MAAM,CAAC,OAAO,KAAK,EAAS,CAAC;AAC7B,MAAM,CAAC,OAAO,CAAC,GAAG,KAAK,EAAE,CAAC;AAE1B,mEAAmE;AAClE,OAA2C,CAAC,OAAO,GAAG,IAAI,CAAC","sourcesContent":["// This file must NOT import other files, because they won't be resolved at runtime.\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nwindow.process ??= {} as any;\nwindow.process.env ??= {};\n\n// Assign true to process.browser for packages like readable-stream\n(process as unknown as { browser: boolean }).browser = true;\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
declare const pageErrors: {
|
|
2
|
+
uncaughtErrors: ErrorEvent[];
|
|
3
|
+
uncaughtRejections: PromiseRejectionEvent[];
|
|
4
|
+
unregister: () => void;
|
|
5
|
+
};
|
|
6
|
+
declare function handleError(event: ErrorEvent): void;
|
|
7
|
+
declare function handleRejection(event: PromiseRejectionEvent): void;
|
|
8
|
+
//# sourceMappingURL=errorHandler.inline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errorHandler.inline.d.ts","sourceRoot":"","sources":["../../src/inlineScripts/errorHandler.inline.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,UAAU;;;;CAOd,CAAC;AAEH,iBAAS,WAAW,CAAC,KAAK,EAAE,UAAU,QAErC;AAED,iBAAS,eAAe,CAAC,KAAK,EAAE,qBAAqB,QAEpD"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// This file must NOT import other files, because they won't be resolved at runtime.
|
|
3
|
+
const pageErrors = (window.__pageErrors = {
|
|
4
|
+
uncaughtErrors: [],
|
|
5
|
+
uncaughtRejections: [],
|
|
6
|
+
unregister: () => {
|
|
7
|
+
window.removeEventListener('error', handleError);
|
|
8
|
+
window.removeEventListener('unhandledrejection', handleRejection);
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
function handleError(event) {
|
|
12
|
+
pageErrors.uncaughtErrors.push(event);
|
|
13
|
+
}
|
|
14
|
+
function handleRejection(event) {
|
|
15
|
+
pageErrors.uncaughtRejections.push(event);
|
|
16
|
+
}
|
|
17
|
+
window.addEventListener('error', handleError);
|
|
18
|
+
window.addEventListener('unhandledrejection', handleRejection);
|
|
19
|
+
//# sourceMappingURL=errorHandler.inline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errorHandler.inline.js","sourceRoot":"","sources":["../../src/inlineScripts/errorHandler.inline.ts"],"names":[],"mappings":";AAAA,oFAAoF;AAEpF,MAAM,UAAU,GAAG,CAAC,MAAM,CAAC,YAAY,GAAG;IACxC,cAAc,EAAE,EAAkB;IAClC,kBAAkB,EAAE,EAA6B;IACjD,UAAU,EAAE,GAAG,EAAE;QACf,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QACjD,MAAM,CAAC,mBAAmB,CAAC,oBAAoB,EAAE,eAAe,CAAC,CAAC;IACpE,CAAC;CACF,CAAC,CAAC;AAEH,SAAS,WAAW,CAAC,KAAiB;IACpC,UAAU,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACxC,CAAC;AAED,SAAS,eAAe,CAAC,KAA4B;IACnD,UAAU,CAAC,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;AAC9C,MAAM,CAAC,gBAAgB,CAAC,oBAAoB,EAAE,eAAe,CAAC,CAAC","sourcesContent":["// This file must NOT import other files, because they won't be resolved at runtime.\n\nconst pageErrors = (window.__pageErrors = {\n uncaughtErrors: [] as ErrorEvent[],\n uncaughtRejections: [] as PromiseRejectionEvent[],\n unregister: () => {\n window.removeEventListener('error', handleError);\n window.removeEventListener('unhandledrejection', handleRejection);\n },\n});\n\nfunction handleError(event: ErrorEvent) {\n pageErrors.uncaughtErrors.push(event);\n}\n\nfunction handleRejection(event: PromiseRejectionEvent) {\n pageErrors.uncaughtRejections.push(event);\n}\n\nwindow.addEventListener('error', handleError);\nwindow.addEventListener('unhandledrejection', handleRejection);\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getBrowserCacheRatio.inline.d.ts","sourceRoot":"","sources":["../../src/inlineScripts/getBrowserCacheRatio.inline.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// This file must NOT import other files (except types), because they won't be resolved at runtime.
|
|
2
|
+
// Typing hack to ensure this name is correct while working around the limitation on runtime imports
|
|
3
|
+
const cloudpackBundleServerUrlCookie = 'cloudpack-bundle-server-url';
|
|
4
|
+
const scriptLoadResults = {};
|
|
5
|
+
function getCookie(cname) {
|
|
6
|
+
const decodedCookies = decodeURIComponent(document.cookie);
|
|
7
|
+
const cookies = decodedCookies.split(';');
|
|
8
|
+
const keyValues = cookies.map((cookie) => cookie.split('='));
|
|
9
|
+
const cookie = keyValues.find(([key]) => key.trim() === cname);
|
|
10
|
+
if (!cookie) {
|
|
11
|
+
throw new Error(`Cookie ${cname} not found`);
|
|
12
|
+
}
|
|
13
|
+
return cookie[1].trim();
|
|
14
|
+
}
|
|
15
|
+
const bundleServerUrl = getCookie(cloudpackBundleServerUrlCookie);
|
|
16
|
+
function isPerformanceResourceTiming(entry) {
|
|
17
|
+
return entry.entryType === 'resource';
|
|
18
|
+
}
|
|
19
|
+
function handlePerformanceEntry(item) {
|
|
20
|
+
if (isPerformanceResourceTiming(item) && item.name.startsWith(bundleServerUrl)) {
|
|
21
|
+
scriptLoadResults[item.name] = item.transferSize === 0 ? 'cache' : 'network';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const perfObserver = new PerformanceObserver((list) => {
|
|
25
|
+
for (const item of list.getEntries()) {
|
|
26
|
+
handlePerformanceEntry(item);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
// Get all the resources that were loaded before this script
|
|
30
|
+
for (const entry of performance.getEntriesByType('resource')) {
|
|
31
|
+
handlePerformanceEntry(entry);
|
|
32
|
+
}
|
|
33
|
+
// Observe all resources that are loaded after this script
|
|
34
|
+
perfObserver.observe({ type: 'resource', buffered: true });
|
|
35
|
+
function getBrowserCacheRatio() {
|
|
36
|
+
const cacheCount = Object.values(scriptLoadResults).filter((source) => source == 'cache').length;
|
|
37
|
+
const ratio = cacheCount / Object.keys(scriptLoadResults).length;
|
|
38
|
+
return ratio;
|
|
39
|
+
}
|
|
40
|
+
window.__cloudpack ??= {};
|
|
41
|
+
window.__cloudpack.getBrowserCacheRatio = getBrowserCacheRatio;
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=getBrowserCacheRatio.inline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getBrowserCacheRatio.inline.js","sourceRoot":"","sources":["../../src/inlineScripts/getBrowserCacheRatio.inline.ts"],"names":[],"mappings":"AAAA,mGAAmG;AAInG,oGAAoG;AACpG,MAAM,8BAA8B,GAA4C,6BAA6B,CAAC;AAE9G,MAAM,iBAAiB,GAAwC,EAAE,CAAC;AAElE,SAAS,SAAS,CAAC,KAAa;IAC9B,MAAM,cAAc,GAAG,kBAAkB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,KAAK,CAAC,CAAC;IAC/D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,UAAU,KAAK,YAAY,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,eAAe,GAAG,SAAS,CAAC,8BAA8B,CAAC,CAAC;AAElE,SAAS,2BAA2B,CAAC,KAAuB;IAC1D,OAAO,KAAK,CAAC,SAAS,KAAK,UAAU,CAAC;AACxC,CAAC;AAED,SAAS,sBAAsB,CAAC,IAAsB;IACpD,IAAI,2BAA2B,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;QAC/E,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/E,CAAC;AACH,CAAC;AAED,MAAM,YAAY,GAAG,IAAI,mBAAmB,CAAC,CAAC,IAAI,EAAE,EAAE;IACpD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;QACrC,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,4DAA4D;AAC5D,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,gBAAgB,CAAC,UAAU,CAAC,EAAE,CAAC;IAC7D,sBAAsB,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC;AAED,0DAA0D;AAC1D,YAAY,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;AAE3D,SAAS,oBAAoB;IAC3B,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,MAAM,CAAC;IACjG,MAAM,KAAK,GAAG,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC;IAEjE,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,WAAW,KAAK,EAA+B,CAAC;AACvD,MAAM,CAAC,WAAW,CAAC,oBAAoB,GAAG,oBAAoB,CAAC","sourcesContent":["// This file must NOT import other files (except types), because they won't be resolved at runtime.\n\nimport type { cookieNames } from '../cookieNames.js';\n\n// Typing hack to ensure this name is correct while working around the limitation on runtime imports\nconst cloudpackBundleServerUrlCookie: (typeof cookieNames)['bundleServerUrl'] = 'cloudpack-bundle-server-url';\n\nconst scriptLoadResults: Record<string, 'cache' | 'network'> = {};\n\nfunction getCookie(cname: string) {\n const decodedCookies = decodeURIComponent(document.cookie);\n const cookies = decodedCookies.split(';');\n const keyValues = cookies.map((cookie) => cookie.split('='));\n const cookie = keyValues.find(([key]) => key.trim() === cname);\n if (!cookie) {\n throw new Error(`Cookie ${cname} not found`);\n }\n\n return cookie[1].trim();\n}\n\nconst bundleServerUrl = getCookie(cloudpackBundleServerUrlCookie);\n\nfunction isPerformanceResourceTiming(entry: PerformanceEntry): entry is PerformanceResourceTiming {\n return entry.entryType === 'resource';\n}\n\nfunction handlePerformanceEntry(item: PerformanceEntry) {\n if (isPerformanceResourceTiming(item) && item.name.startsWith(bundleServerUrl)) {\n scriptLoadResults[item.name] = item.transferSize === 0 ? 'cache' : 'network';\n }\n}\n\nconst perfObserver = new PerformanceObserver((list) => {\n for (const item of list.getEntries()) {\n handlePerformanceEntry(item);\n }\n});\n\n// Get all the resources that were loaded before this script\nfor (const entry of performance.getEntriesByType('resource')) {\n handlePerformanceEntry(entry);\n}\n\n// Observe all resources that are loaded after this script\nperfObserver.observe({ type: 'resource', buffered: true });\n\nfunction getBrowserCacheRatio() {\n const cacheCount = Object.values(scriptLoadResults).filter((source) => source == 'cache').length;\n const ratio = cacheCount / Object.keys(scriptLoadResults).length;\n\n return ratio;\n}\n\nwindow.__cloudpack ??= {} as typeof window.__cloudpack;\nwindow.__cloudpack.getBrowserCacheRatio = getBrowserCacheRatio;\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getInlineScripts.d.ts","sourceRoot":"","sources":["../../src/inlineScripts/getInlineScripts.ts"],"names":[],"mappings":"AAcA,wBAAsB,gBAAgB,sBAQrC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import fsPromises from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dynamicImportExtension } from '../utilities/dynamicImportExtension.js';
|
|
5
|
+
const currentPath = fileURLToPath(path.dirname(import.meta.url));
|
|
6
|
+
let inlineScripts;
|
|
7
|
+
// List of files that we will load on demand.
|
|
8
|
+
const inlineScriptFiles = ['defineProcess', 'errorHandler', 'getBrowserCacheRatio', 'getPageLoadTime'].map((file) => `${file}.inline${dynamicImportExtension}`);
|
|
9
|
+
export async function getInlineScripts() {
|
|
10
|
+
if (!inlineScripts) {
|
|
11
|
+
inlineScripts = await Promise.all(inlineScriptFiles.map((filename) => fsPromises.readFile(path.join(currentPath, filename), 'utf-8')));
|
|
12
|
+
}
|
|
13
|
+
return inlineScripts;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=getInlineScripts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getInlineScripts.js","sourceRoot":"","sources":["../../src/inlineScripts/getInlineScripts.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,aAAa,CAAC;AACrC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAEhF,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAEjE,IAAI,aAAmC,CAAC;AAExC,6CAA6C;AAC7C,MAAM,iBAAiB,GAAG,CAAC,eAAe,EAAE,cAAc,EAAE,sBAAsB,EAAE,iBAAiB,CAAC,CAAC,GAAG,CACxG,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,UAAU,sBAAsB,EAAE,CACpD,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,aAAa,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,iBAAiB,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC,CACpG,CAAC;IACJ,CAAC;IAED,OAAO,aAAa,CAAC;AACvB,CAAC","sourcesContent":["import fsPromises from 'fs/promises';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\nimport { dynamicImportExtension } from '../utilities/dynamicImportExtension.js';\n\nconst currentPath = fileURLToPath(path.dirname(import.meta.url));\n\nlet inlineScripts: string[] | undefined;\n\n// List of files that we will load on demand.\nconst inlineScriptFiles = ['defineProcess', 'errorHandler', 'getBrowserCacheRatio', 'getPageLoadTime'].map(\n (file) => `${file}.inline${dynamicImportExtension}`,\n);\n\nexport async function getInlineScripts() {\n if (!inlineScripts) {\n inlineScripts = await Promise.all(\n inlineScriptFiles.map((filename) => fsPromises.readFile(path.join(currentPath, filename), 'utf-8')),\n );\n }\n\n return inlineScripts;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getPageLoadTime.inline.d.ts","sourceRoot":"","sources":["../../src/inlineScripts/getPageLoadTime.inline.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// This file must NOT import other files (except types), because they won't be resolved at runtime.
|
|
2
|
+
// Typing hack to ensure this ID is correct while working around the limitation on runtime imports
|
|
3
|
+
const overlayRootDivId = 'cloudpack-overlay-root';
|
|
4
|
+
/**
|
|
5
|
+
* Performance mark name for page load time.
|
|
6
|
+
* This makes it easier to see the page load time in the performance tab in dev tools
|
|
7
|
+
*/
|
|
8
|
+
const pageLoadMarker = 'CLOUDPACK_PAGE_LOAD_TIME';
|
|
9
|
+
// The below dom elements will be ignored by mutation observer
|
|
10
|
+
const excludedIds = [overlayRootDivId];
|
|
11
|
+
function debounce(func, wait) {
|
|
12
|
+
let timeout;
|
|
13
|
+
return function (...args) {
|
|
14
|
+
const later = () => {
|
|
15
|
+
timeout = null;
|
|
16
|
+
func(...args);
|
|
17
|
+
};
|
|
18
|
+
if (timeout) {
|
|
19
|
+
clearTimeout(timeout);
|
|
20
|
+
}
|
|
21
|
+
timeout = setTimeout(later, wait);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function hasMutationNonExcludedNodes(mutation) {
|
|
25
|
+
return [...mutation.addedNodes].some((node) => {
|
|
26
|
+
const id = node.id;
|
|
27
|
+
if (id) {
|
|
28
|
+
return !excludedIds.includes(id);
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const pageLoadTimePromise = new Promise((resolve) => {
|
|
34
|
+
const observer = new MutationObserver(domChanged);
|
|
35
|
+
function reportLastAnimationTime(time) {
|
|
36
|
+
// Stop observing the DOM
|
|
37
|
+
observer.disconnect();
|
|
38
|
+
// Resolve the promise with the page load time
|
|
39
|
+
resolve(time);
|
|
40
|
+
}
|
|
41
|
+
const debouncedReportLastAnimationTime = debounce(reportLastAnimationTime, 2000 /* wait ms */);
|
|
42
|
+
function domChanged(mutations) {
|
|
43
|
+
const shouldProcess = mutations.filter(hasMutationNonExcludedNodes).length > 0;
|
|
44
|
+
if (shouldProcess) {
|
|
45
|
+
requestAnimationFrame(() => {
|
|
46
|
+
// Get the current time in ms since the time when navigation has started in window
|
|
47
|
+
const time = performance.now();
|
|
48
|
+
// Clear previous marks
|
|
49
|
+
performance.clearMarks(pageLoadMarker);
|
|
50
|
+
// Mark the page load time
|
|
51
|
+
performance.mark(pageLoadMarker);
|
|
52
|
+
// Report the page load time
|
|
53
|
+
debouncedReportLastAnimationTime(time);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
observer.observe(document.body, {
|
|
58
|
+
childList: true,
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
window.__cloudpack ??= {};
|
|
62
|
+
window.__cloudpack.getPageLoadTime = () => pageLoadTimePromise;
|
|
63
|
+
export {};
|
|
64
|
+
//# sourceMappingURL=getPageLoadTime.inline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getPageLoadTime.inline.js","sourceRoot":"","sources":["../../src/inlineScripts/getPageLoadTime.inline.ts"],"names":[],"mappings":"AAAA,mGAAmG;AAInG,kGAAkG;AAClG,MAAM,gBAAgB,GAAgC,wBAAwB,CAAC;AAE/E;;;GAGG;AACH,MAAM,cAAc,GAAG,0BAA0B,CAAC;AAElD,8DAA8D;AAC9D,MAAM,WAAW,GAAa,CAAC,gBAAgB,CAAC,CAAC;AAIjD,SAAS,QAAQ,CAAsB,IAAqB,EAAE,IAAY;IACxE,IAAI,OAA6C,CAAC;IAElD,OAAO,UAAU,GAAG,IAAO;QACzB,MAAM,KAAK,GAAG,GAAG,EAAE;YACjB,OAAO,GAAG,IAAI,CAAC;YACf,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QAChB,CAAC,CAAC;QAEF,IAAI,OAAO,EAAE,CAAC;YACZ,YAAY,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;QACD,OAAO,GAAG,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,2BAA2B,CAAC,QAAwB;IAC3D,OAAO,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;QAC5C,MAAM,EAAE,GAAI,IAAwB,CAAC,EAAE,CAAC;QAExC,IAAI,EAAE,EAAE,CAAC;YACP,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACnC,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,mBAAmB,GAAG,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;IAC1D,MAAM,QAAQ,GAAG,IAAI,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAElD,SAAS,uBAAuB,CAAC,IAAY;QAC3C,yBAAyB;QACzB,QAAQ,CAAC,UAAU,EAAE,CAAC;QAEtB,8CAA8C;QAC9C,OAAO,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IAED,MAAM,gCAAgC,GAAG,QAAQ,CAAC,uBAAuB,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;IAE/F,SAAS,UAAU,CAAC,SAA2B;QAC7C,MAAM,aAAa,GAAG,SAAS,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;QAE/E,IAAI,aAAa,EAAE,CAAC;YAClB,qBAAqB,CAAC,GAAG,EAAE;gBACzB,kFAAkF;gBAClF,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;gBAE/B,uBAAuB;gBACvB,WAAW,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;gBAEvC,0BAA0B;gBAC1B,WAAW,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;gBAEjC,4BAA4B;gBAC5B,gCAAgC,CAAC,IAAI,CAAC,CAAC;YACzC,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE;QAC9B,SAAS,EAAE,IAAI;KAChB,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,WAAW,KAAK,EAA+B,CAAC;AACvD,MAAM,CAAC,WAAW,CAAC,eAAe,GAAG,GAAG,EAAE,CAAC,mBAAmB,CAAC","sourcesContent":["// This file must NOT import other files (except types), because they won't be resolved at runtime.\n\nimport type { elementIds } from '@ms-cloudpack/overlay/constants';\n\n// Typing hack to ensure this ID is correct while working around the limitation on runtime imports\nconst overlayRootDivId: (typeof elementIds)['root'] = 'cloudpack-overlay-root';\n\n/**\n * Performance mark name for page load time.\n * This makes it easier to see the page load time in the performance tab in dev tools\n */\nconst pageLoadMarker = 'CLOUDPACK_PAGE_LOAD_TIME';\n\n// The below dom elements will be ignored by mutation observer\nconst excludedIds: string[] = [overlayRootDivId];\n\ntype DebounceFunc<T extends unknown[]> = (...args: T) => void;\n\nfunction debounce<T extends unknown[]>(func: DebounceFunc<T>, wait: number): DebounceFunc<T> {\n let timeout: ReturnType<typeof setTimeout> | null;\n\n return function (...args: T) {\n const later = () => {\n timeout = null;\n func(...args);\n };\n\n if (timeout) {\n clearTimeout(timeout);\n }\n timeout = setTimeout(later, wait);\n };\n}\n\nfunction hasMutationNonExcludedNodes(mutation: MutationRecord) {\n return [...mutation.addedNodes].some((node) => {\n const id = (node as { id?: string }).id;\n\n if (id) {\n return !excludedIds.includes(id);\n }\n\n return true;\n });\n}\n\nconst pageLoadTimePromise = new Promise<number>((resolve) => {\n const observer = new MutationObserver(domChanged);\n\n function reportLastAnimationTime(time: number) {\n // Stop observing the DOM\n observer.disconnect();\n\n // Resolve the promise with the page load time\n resolve(time);\n }\n\n const debouncedReportLastAnimationTime = debounce(reportLastAnimationTime, 2000 /* wait ms */);\n\n function domChanged(mutations: MutationRecord[]) {\n const shouldProcess = mutations.filter(hasMutationNonExcludedNodes).length > 0;\n\n if (shouldProcess) {\n requestAnimationFrame(() => {\n // Get the current time in ms since the time when navigation has started in window\n const time = performance.now();\n\n // Clear previous marks\n performance.clearMarks(pageLoadMarker);\n\n // Mark the page load time\n performance.mark(pageLoadMarker);\n\n // Report the page load time\n debouncedReportLastAnimationTime(time);\n });\n }\n }\n\n observer.observe(document.body, {\n childList: true,\n });\n});\n\nwindow.__cloudpack ??= {} as typeof window.__cloudpack;\nwindow.__cloudpack.getPageLoadTime = () => pageLoadTimePromise;\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Response } from '@ms-cloudpack/create-express-app';
|
|
2
|
+
import type { ApiServer, Session } from '@ms-cloudpack/api-server';
|
|
3
|
+
import type { BundleServer } from '@ms-cloudpack/bundle-server';
|
|
4
|
+
/**
|
|
5
|
+
* Set the Cloudpack headers and cookies in the response.
|
|
6
|
+
*/
|
|
7
|
+
export declare function setHeaders(params: {
|
|
8
|
+
res: Response;
|
|
9
|
+
session: Session;
|
|
10
|
+
apiServer: ApiServer;
|
|
11
|
+
bundleServer: BundleServer;
|
|
12
|
+
}): void;
|
|
13
|
+
//# sourceMappingURL=setHeaders.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setHeaders.d.ts","sourceRoot":"","sources":["../src/setHeaders.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAGhE;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE;IACjC,GAAG,EAAE,QAAQ,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,EAAE,YAAY,CAAC;CAC5B,QAWA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { cookieNames, getCloudpackSessionVersionCookie } from './cookieNames.js';
|
|
2
|
+
/**
|
|
3
|
+
* Set the Cloudpack headers and cookies in the response.
|
|
4
|
+
*/
|
|
5
|
+
export function setHeaders(params) {
|
|
6
|
+
const { res, session, apiServer, bundleServer } = params;
|
|
7
|
+
res.header('Cache-Control', 'no-cache');
|
|
8
|
+
res.header('Access-Control-Allow-Origin', '*');
|
|
9
|
+
res.cookie(cookieNames.sessionId, session.id);
|
|
10
|
+
res.cookie(cookieNames.sessionSequence, session.sequence);
|
|
11
|
+
res.cookie(getCloudpackSessionVersionCookie(session.projectName), session.getSessionVersion());
|
|
12
|
+
res.cookie(cookieNames.apiUrl, apiServer.url);
|
|
13
|
+
res.cookie(cookieNames.bundleServerUrl, bundleServer.url);
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=setHeaders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setHeaders.js","sourceRoot":"","sources":["../src/setHeaders.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,gCAAgC,EAAE,MAAM,kBAAkB,CAAC;AAEjF;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,MAK1B;IACC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC;IAEzD,GAAG,CAAC,MAAM,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;IACxC,GAAG,CAAC,MAAM,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;IAE/C,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IAC9C,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,eAAe,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC1D,GAAG,CAAC,MAAM,CAAC,gCAAgC,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAC/F,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;IAC9C,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,eAAe,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC;AAC5D,CAAC","sourcesContent":["import type { Response } from '@ms-cloudpack/create-express-app';\nimport type { ApiServer, Session } from '@ms-cloudpack/api-server';\nimport type { BundleServer } from '@ms-cloudpack/bundle-server';\nimport { cookieNames, getCloudpackSessionVersionCookie } from './cookieNames.js';\n\n/**\n * Set the Cloudpack headers and cookies in the response.\n */\nexport function setHeaders(params: {\n res: Response;\n session: Session;\n apiServer: ApiServer;\n bundleServer: BundleServer;\n}) {\n const { res, session, apiServer, bundleServer } = params;\n\n res.header('Cache-Control', 'no-cache');\n res.header('Access-Control-Allow-Origin', '*');\n\n res.cookie(cookieNames.sessionId, session.id);\n res.cookie(cookieNames.sessionSequence, session.sequence);\n res.cookie(getCloudpackSessionVersionCookie(session.projectName), session.getSessionVersion());\n res.cookie(cookieNames.apiUrl, apiServer.url);\n res.cookie(cookieNames.bundleServerUrl, bundleServer.url);\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { BundleServer } from '@ms-cloudpack/bundle-server';
|
|
2
|
+
import type { PackageDefinitionsCache } from '@ms-cloudpack/bundler-types';
|
|
3
|
+
import { type TaskReporter } from '@ms-cloudpack/task-reporter';
|
|
4
|
+
import type { CloudpackConfig, PackageJson } from '@ms-cloudpack/config-types';
|
|
5
|
+
import type { ApiServer, Session } from '@ms-cloudpack/api-server';
|
|
6
|
+
import type { PackageImportPaths } from '@ms-cloudpack/package-utilities';
|
|
7
|
+
import type { PackageHashes } from '@ms-cloudpack/package-hashes';
|
|
8
|
+
import type { AppServer } from './types/AppServer.js';
|
|
9
|
+
/**
|
|
10
|
+
* The app server hosts the appropriate routes for the web app, primarily returning html content
|
|
11
|
+
* which loads resources from the bundle server.
|
|
12
|
+
*
|
|
13
|
+
* Separating the app server from the bundle service keeps the routes separate - the app server
|
|
14
|
+
* can support whichever routes the app needs, while the bundle server can provide package
|
|
15
|
+
* assets in various forms using its own routing.
|
|
16
|
+
*/
|
|
17
|
+
export declare function startAppServer({ session, bundleServer, apiServer, definition, config, packages, packageImportPaths, reporter, packageHashes, }: {
|
|
18
|
+
session: Session;
|
|
19
|
+
definition: PackageJson;
|
|
20
|
+
bundleServer: BundleServer;
|
|
21
|
+
apiServer: ApiServer;
|
|
22
|
+
config: CloudpackConfig;
|
|
23
|
+
packages: PackageDefinitionsCache;
|
|
24
|
+
packageImportPaths: PackageImportPaths;
|
|
25
|
+
reporter: TaskReporter;
|
|
26
|
+
packageHashes: PackageHashes;
|
|
27
|
+
}): Promise<AppServer>;
|
|
28
|
+
//# sourceMappingURL=startAppServer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startAppServer.d.ts","sourceRoot":"","sources":["../src/startAppServer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAG3E,OAAO,EAAE,KAAK,YAAY,EAAQ,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC/E,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAAC,EACnC,OAAO,EACP,YAAY,EACZ,SAAS,EACT,UAAU,EACV,MAA8B,EAC9B,QAAQ,EACR,kBAAkB,EAClB,QAAQ,EACR,aAAa,GACd,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,WAAW,CAAC;IACxB,YAAY,EAAE,YAAY,CAAC;IAC3B,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,EAAE,eAAe,CAAC;IACxB,QAAQ,EAAE,uBAAuB,CAAC;IAClC,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,QAAQ,EAAE,YAAY,CAAC;IACvB,aAAa,EAAE,aAAa,CAAC;CAC9B,GAAG,OAAO,CAAC,SAAS,CAAC,CAgDrB"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { createExpressApp } from '@ms-cloudpack/create-express-app';
|
|
2
|
+
import { createRoutes } from './createRoutes.js';
|
|
3
|
+
import { exitIfPortUnavailable } from './exitIfPortUnavailable.js';
|
|
4
|
+
import { cyan } from '@ms-cloudpack/task-reporter';
|
|
5
|
+
/**
|
|
6
|
+
* The app server hosts the appropriate routes for the web app, primarily returning html content
|
|
7
|
+
* which loads resources from the bundle server.
|
|
8
|
+
*
|
|
9
|
+
* Separating the app server from the bundle service keeps the routes separate - the app server
|
|
10
|
+
* can support whichever routes the app needs, while the bundle server can provide package
|
|
11
|
+
* assets in various forms using its own routing.
|
|
12
|
+
*/
|
|
13
|
+
export async function startAppServer({ session, bundleServer, apiServer, definition, config = {}, packages, packageImportPaths, reporter, packageHashes, }) {
|
|
14
|
+
const { devServer = {} } = config || {};
|
|
15
|
+
// Read the port from the config file or default to array of ports.
|
|
16
|
+
const requireSpecifiedPort = devServer?.port !== undefined;
|
|
17
|
+
const ports = devServer?.port ?? [5000, 5001, 5002, 5003];
|
|
18
|
+
// Directory to serve as plain static assets. Defaults to "public".
|
|
19
|
+
// Can be overridden by setting the 'publicDir' option in the cloudpack config.
|
|
20
|
+
// const publicDir = path.resolve(devServer.publicPath ?? 'public');
|
|
21
|
+
const task = reporter.addTask(`Starting app server for "${definition.name}"`);
|
|
22
|
+
try {
|
|
23
|
+
const { close, port, url } = await createExpressApp({
|
|
24
|
+
portRange: ports,
|
|
25
|
+
requireSpecifiedPort,
|
|
26
|
+
hostname: devServer?.domain,
|
|
27
|
+
sslOptions: config.devServer?.https,
|
|
28
|
+
// publicDir,
|
|
29
|
+
setupCallback: (app, appUrl) => createRoutes({
|
|
30
|
+
app,
|
|
31
|
+
url: appUrl,
|
|
32
|
+
session,
|
|
33
|
+
definition,
|
|
34
|
+
bundleServer,
|
|
35
|
+
apiServer,
|
|
36
|
+
config,
|
|
37
|
+
packages,
|
|
38
|
+
packageImportPaths,
|
|
39
|
+
packageHashes,
|
|
40
|
+
}),
|
|
41
|
+
});
|
|
42
|
+
task.complete({ message: `Available @ ${cyan(url)}`, forceShow: true });
|
|
43
|
+
return {
|
|
44
|
+
close,
|
|
45
|
+
port,
|
|
46
|
+
url,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
task.complete({ status: 'fail', message: 'Failed to start app server', forceShow: true });
|
|
51
|
+
exitIfPortUnavailable(err, session.projectName, ports);
|
|
52
|
+
throw err;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=startAppServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startAppServer.js","sourceRoot":"","sources":["../src/startAppServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAGpE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAqB,IAAI,EAAE,MAAM,6BAA6B,CAAC;AAOtE;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,EACnC,OAAO,EACP,YAAY,EACZ,SAAS,EACT,UAAU,EACV,MAAM,GAAG,EAAqB,EAC9B,QAAQ,EACR,kBAAkB,EAClB,QAAQ,EACR,aAAa,GAWd;IACC,MAAM,EAAE,SAAS,GAAG,EAAE,EAAE,GAAG,MAAM,IAAK,EAAsB,CAAC;IAE7D,mEAAmE;IACnE,MAAM,oBAAoB,GAAG,SAAS,EAAE,IAAI,KAAK,SAAS,CAAC;IAC3D,MAAM,KAAK,GAAG,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAE1D,mEAAmE;IACnE,+EAA+E;IAC/E,oEAAoE;IAEpE,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,4BAA4B,UAAU,CAAC,IAAI,GAAG,CAAC,CAAC;IAE9E,IAAI,CAAC;QACH,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,gBAAgB,CAAC;YAClD,SAAS,EAAE,KAAK;YAChB,oBAAoB;YACpB,QAAQ,EAAE,SAAS,EAAE,MAAM;YAC3B,UAAU,EAAE,MAAM,CAAC,SAAS,EAAE,KAAK;YACnC,cAAc;YACd,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAC7B,YAAY,CAAC;gBACX,GAAG;gBACH,GAAG,EAAE,MAAM;gBACX,OAAO;gBACP,UAAU;gBACV,YAAY;gBACZ,SAAS;gBACT,MAAM;gBACN,QAAQ;gBACR,kBAAkB;gBAClB,aAAa;aACd,CAAC;SACL,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,eAAe,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAExE,OAAO;YACL,KAAK;YACL,IAAI;YACJ,GAAG;SACJ,CAAC;IACJ,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,4BAA4B,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1F,qBAAqB,CAAC,GAAG,EAAE,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QAEvD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC","sourcesContent":["import { createExpressApp } from '@ms-cloudpack/create-express-app';\nimport type { BundleServer } from '@ms-cloudpack/bundle-server';\nimport type { PackageDefinitionsCache } from '@ms-cloudpack/bundler-types';\nimport { createRoutes } from './createRoutes.js';\nimport { exitIfPortUnavailable } from './exitIfPortUnavailable.js';\nimport { type TaskReporter, cyan } from '@ms-cloudpack/task-reporter';\nimport type { CloudpackConfig, PackageJson } from '@ms-cloudpack/config-types';\nimport type { ApiServer, Session } from '@ms-cloudpack/api-server';\nimport type { PackageImportPaths } from '@ms-cloudpack/package-utilities';\nimport type { PackageHashes } from '@ms-cloudpack/package-hashes';\nimport type { AppServer } from './types/AppServer.js';\n\n/**\n * The app server hosts the appropriate routes for the web app, primarily returning html content\n * which loads resources from the bundle server.\n *\n * Separating the app server from the bundle service keeps the routes separate - the app server\n * can support whichever routes the app needs, while the bundle server can provide package\n * assets in various forms using its own routing.\n */\nexport async function startAppServer({\n session,\n bundleServer,\n apiServer,\n definition,\n config = {} as CloudpackConfig,\n packages,\n packageImportPaths,\n reporter,\n packageHashes,\n}: {\n session: Session;\n definition: PackageJson;\n bundleServer: BundleServer;\n apiServer: ApiServer;\n config: CloudpackConfig;\n packages: PackageDefinitionsCache;\n packageImportPaths: PackageImportPaths;\n reporter: TaskReporter;\n packageHashes: PackageHashes;\n}): Promise<AppServer> {\n const { devServer = {} } = config || ({} as CloudpackConfig);\n\n // Read the port from the config file or default to array of ports.\n const requireSpecifiedPort = devServer?.port !== undefined;\n const ports = devServer?.port ?? [5000, 5001, 5002, 5003];\n\n // Directory to serve as plain static assets. Defaults to \"public\".\n // Can be overridden by setting the 'publicDir' option in the cloudpack config.\n // const publicDir = path.resolve(devServer.publicPath ?? 'public');\n\n const task = reporter.addTask(`Starting app server for \"${definition.name}\"`);\n\n try {\n const { close, port, url } = await createExpressApp({\n portRange: ports,\n requireSpecifiedPort,\n hostname: devServer?.domain,\n sslOptions: config.devServer?.https,\n // publicDir,\n setupCallback: (app, appUrl) =>\n createRoutes({\n app,\n url: appUrl,\n session,\n definition,\n bundleServer,\n apiServer,\n config,\n packages,\n packageImportPaths,\n packageHashes,\n }),\n });\n\n task.complete({ message: `Available @ ${cyan(url)}`, forceShow: true });\n\n return {\n close,\n port,\n url,\n };\n } catch (err: unknown) {\n task.complete({ status: 'fail', message: 'Failed to start app server', forceShow: true });\n exitIfPortUnavailable(err, session.projectName, ports);\n\n throw err;\n }\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// This file is read by tools that parse documentation comments conforming to the TSDoc standard.
|
|
2
|
+
// It should be published with your NPM package. It should not be tracked by Git.
|
|
3
|
+
{
|
|
4
|
+
"tsdocVersion": "0.12",
|
|
5
|
+
"toolPackages": [
|
|
6
|
+
{
|
|
7
|
+
"packageName": "@microsoft/api-extractor",
|
|
8
|
+
"packageVersion": "7.39.0"
|
|
9
|
+
}
|
|
10
|
+
]
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppServer.d.ts","sourceRoot":"","sources":["../../src/types/AppServer.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppServer.js","sourceRoot":"","sources":["../../src/types/AppServer.ts"],"names":[],"mappings":"","sourcesContent":["export interface AppServer {\n url: string;\n port: number;\n close: () => Promise<void>;\n}\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Correct file extension (`.js` or `.ts`) depending on the context the code is running in:
|
|
3
|
+
* compiled (`.js`) or Jest test in this package (`.ts`).
|
|
4
|
+
*/
|
|
5
|
+
export declare const dynamicImportExtension: string;
|
|
6
|
+
//# sourceMappingURL=dynamicImportExtension.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dynamicImportExtension.d.ts","sourceRoot":"","sources":["../../src/utilities/dynamicImportExtension.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,eAAO,MAAM,sBAAsB,QAA+C,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
/**
|
|
4
|
+
* Correct file extension (`.js` or `.ts`) depending on the context the code is running in:
|
|
5
|
+
* compiled (`.js`) or Jest test in this package (`.ts`).
|
|
6
|
+
*/
|
|
7
|
+
export const dynamicImportExtension = path.extname(fileURLToPath(import.meta.url));
|
|
8
|
+
//# sourceMappingURL=dynamicImportExtension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dynamicImportExtension.js","sourceRoot":"","sources":["../../src/utilities/dynamicImportExtension.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC","sourcesContent":["import path from 'path';\nimport { fileURLToPath } from 'url';\n\n/**\n * Correct file extension (`.js` or `.ts`) depending on the context the code is running in:\n * compiled (`.js`) or Jest test in this package (`.ts`).\n */\nexport const dynamicImportExtension = path.extname(fileURLToPath(import.meta.url));\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ms-cloudpack/app-server",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "An implementation of the App server for Cloudpack.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"types": "./lib/index.d.ts",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"source": "./src/index.ts",
|
|
12
|
+
"types": "./lib/index.d.ts",
|
|
13
|
+
"import": "./lib/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@ms-cloudpack/api-server": "^0.29.8",
|
|
18
|
+
"@ms-cloudpack/bundle-server": "^0.2.1",
|
|
19
|
+
"@ms-cloudpack/bundler-types": "^0.23.9",
|
|
20
|
+
"@ms-cloudpack/config-types": "^0.4.0",
|
|
21
|
+
"@ms-cloudpack/create-express-app": "^1.3.16",
|
|
22
|
+
"@ms-cloudpack/overlay": "^0.16.49",
|
|
23
|
+
"@ms-cloudpack/package-hashes": "^0.3.9",
|
|
24
|
+
"@ms-cloudpack/package-utilities": "^5.7.8",
|
|
25
|
+
"@ms-cloudpack/path-string-parsing": "^1.1.3",
|
|
26
|
+
"@ms-cloudpack/task-reporter": "^0.10.2",
|
|
27
|
+
"jsdom": "^22.0.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@ms-cloudpack/eslint-plugin-internal": "*",
|
|
31
|
+
"@ms-cloudpack/scripts": "*",
|
|
32
|
+
"@ms-cloudpack/test-utilities": "*"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"api": "cloudpack-scripts api",
|
|
36
|
+
"build:watch": "cloudpack-scripts build-watch",
|
|
37
|
+
"build": "cloudpack-scripts build",
|
|
38
|
+
"lint:update": "cloudpack-scripts lint-update",
|
|
39
|
+
"lint": "cloudpack-scripts lint"
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"lib/**/!(*.test.*)"
|
|
43
|
+
]
|
|
44
|
+
}
|