@appsemble/node-utils 0.20.44 → 0.21.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/README.md +3 -3
- package/app.d.ts +17 -0
- package/app.js +49 -0
- package/csv.d.ts +2 -0
- package/csv.js +55 -0
- package/fs.d.ts +3 -3
- package/fs.test.js +5 -5
- package/getAppsembleMessages.d.ts +9 -0
- package/getAppsembleMessages.js +32 -0
- package/handleError.test.js +4 -4
- package/icon.d.ts +44 -0
- package/icon.js +69 -0
- package/index.d.ts +22 -1
- package/index.js +22 -1
- package/interceptors.test.js +5 -6
- package/jsonschema.d.ts +2 -0
- package/jsonschema.js +7 -0
- package/koa.d.ts +6 -0
- package/koa.js +27 -0
- package/logger.js +3 -0
- package/mergeMessages.d.ts +9 -0
- package/mergeMessages.js +17 -0
- package/middleware/boom.d.ts +7 -0
- package/middleware/boom.js +32 -0
- package/middleware/boom.test.js +58 -0
- package/middleware/conditional.d.ts +2 -0
- package/middleware/conditional.js +9 -0
- package/middleware/frontend.d.ts +3 -0
- package/middleware/frontend.js +40 -0
- package/{loggerMiddleware.js → middleware/loggerMiddleware.js} +1 -1
- package/{loggerMiddleware.test.js → middleware/loggerMiddleware.test.js} +14 -15
- package/odata.d.ts +18 -0
- package/odata.js +45 -0
- package/organizationBlocklist.d.ts +4 -0
- package/organizationBlocklist.js +5 -0
- package/package.json +41 -4
- package/parsers.d.ts +3 -0
- package/parsers.js +18 -0
- package/readAsset.d.ts +13 -0
- package/readAsset.js +6 -0
- package/readFileOrString.d.ts +1 -1
- package/render.d.ts +21 -0
- package/render.js +46 -0
- package/resource.d.ts +43 -0
- package/resource.js +172 -0
- package/scim.d.ts +26 -0
- package/scim.js +32 -0
- package/server/controllers/action.d.ts +7 -0
- package/server/controllers/action.js +129 -0
- package/server/controllers/appMembers.d.ts +3 -0
- package/server/controllers/appMembers.js +19 -0
- package/server/controllers/appMessages.d.ts +3 -0
- package/server/controllers/appMessages.js +75 -0
- package/server/controllers/assets.d.ts +5 -0
- package/server/controllers/assets.js +74 -0
- package/server/controllers/index.d.ts +7 -0
- package/server/controllers/index.js +8 -0
- package/server/controllers/oauth2Provider.d.ts +3 -0
- package/server/controllers/oauth2Provider.js +7 -0
- package/server/controllers/resources.d.ts +8 -0
- package/server/controllers/resources.js +273 -0
- package/server/controllers/resources.test.d.ts +1 -0
- package/server/controllers/resources.test.js +552 -0
- package/server/controllers/teams.d.ts +3 -0
- package/server/controllers/teams.js +12 -0
- package/server/routes/appRouter/blockAssetHandler.d.ts +3 -0
- package/server/routes/appRouter/blockAssetHandler.js +13 -0
- package/server/routes/appRouter/blockCssHandler.d.ts +3 -0
- package/server/routes/appRouter/blockCssHandler.js +15 -0
- package/server/routes/appRouter/bulmaHandler.d.ts +3 -0
- package/server/routes/appRouter/bulmaHandler.js +116 -0
- package/server/routes/appRouter/cssHandler.d.ts +3 -0
- package/server/routes/appRouter/cssHandler.js +12 -0
- package/server/routes/appRouter/iconHandler.d.ts +3 -0
- package/server/routes/appRouter/iconHandler.js +23 -0
- package/server/routes/appRouter/index.d.ts +3 -0
- package/server/routes/appRouter/index.js +85 -0
- package/server/routes/appRouter/indexHandler.d.ts +5 -0
- package/server/routes/appRouter/indexHandler.js +69 -0
- package/server/routes/appRouter/manifestHandler.d.ts +3 -0
- package/server/routes/appRouter/manifestHandler.js +47 -0
- package/server/routes/appRouter/robotsHandler.d.ts +2 -0
- package/server/routes/appRouter/robotsHandler.js +6 -0
- package/server/routes/appRouter/screenshotHandler.d.ts +3 -0
- package/server/routes/appRouter/screenshotHandler.js +19 -0
- package/server/routes/appRouter/serviceWorkerHandler.d.ts +3 -0
- package/server/routes/appRouter/serviceWorkerHandler.js +18 -0
- package/server/routes/appRouter/staticHandler.d.ts +2 -0
- package/server/routes/appRouter/staticHandler.js +9 -0
- package/server/types.d.ts +356 -0
- package/server/types.js +2 -0
- package/testFixtures.d.ts +3 -3
- package/testFixtures.js +1 -1
- package/tinyRouter.d.ts +16 -0
- package/tinyRouter.js +34 -0
- package/tinyRouter.test.d.ts +1 -0
- package/tinyRouter.test.js +85 -0
- package/vitest.config.d.ts +2 -0
- package/vitest.setup.d.ts +1 -0
- package/{jest.setup.js → vitest.setup.js} +1 -1
- /package/{jest.setup.d.ts → middleware/boom.test.d.ts} +0 -0
- /package/{loggerMiddleware.d.ts → middleware/loggerMiddleware.d.ts} +0 -0
- /package/{loggerMiddleware.test.d.ts → middleware/loggerMiddleware.test.d.ts} +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { logger } from '@appsemble/node-utils';
|
|
3
|
+
import { baseTheme } from '@appsemble/utils';
|
|
4
|
+
import bulma from 'bulma/package.json' assert { type: 'json' };
|
|
5
|
+
import { renderSync } from 'sass';
|
|
6
|
+
import stripBom from 'strip-bom';
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
const bulmaPath = require.resolve('bulma/bulma.sass').replace(/\\/g, '/');
|
|
9
|
+
const functionPath = require.resolve('bulma/sass/utilities/functions.sass').replace(/\\/g, '/');
|
|
10
|
+
const checkRadioPath = require.resolve('bulma-checkradio/src/sass/index.sass').replace(/\\/g, '/');
|
|
11
|
+
const bulmaSliderPath = require.resolve('bulma-slider/src/sass/index.sass').replace(/\\/g, '/');
|
|
12
|
+
const bulmaSwitchPath = require.resolve('bulma-switch/src/sass/index.sass').replace(/\\/g, '/');
|
|
13
|
+
function getQueryParameter(value) {
|
|
14
|
+
return Array.isArray(value) ? value[0] : value;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Process SASS styles based on given parameters.
|
|
18
|
+
*
|
|
19
|
+
* @param theme The theme object to turn into a SASS file.
|
|
20
|
+
* @returns SASS string containing the base Appsemble style augmented by user parameters.
|
|
21
|
+
*/
|
|
22
|
+
function processStyle(theme) {
|
|
23
|
+
const fontSource = theme.fontSource || baseTheme.font.source;
|
|
24
|
+
const fontFamily = theme.fontFamily || baseTheme.font.family;
|
|
25
|
+
let googleFontsImport = '';
|
|
26
|
+
if (fontSource === 'google') {
|
|
27
|
+
const googleFontsUrl = new URL('https://fonts.googleapis.com/css');
|
|
28
|
+
googleFontsUrl.searchParams.set('display', 'swap');
|
|
29
|
+
googleFontsUrl.searchParams.set('family', fontFamily);
|
|
30
|
+
googleFontsImport = `@import url(${googleFontsUrl});`;
|
|
31
|
+
}
|
|
32
|
+
return `
|
|
33
|
+
@charset "utf-8";
|
|
34
|
+
${googleFontsImport}
|
|
35
|
+
$family-sans-serif: '${fontFamily}', sans-serif !default;
|
|
36
|
+
$primary: ${theme.primaryColor || baseTheme.primaryColor};
|
|
37
|
+
$link: ${theme.linkColor || baseTheme.linkColor};
|
|
38
|
+
$info: ${theme.infoColor || baseTheme.infoColor};
|
|
39
|
+
$success: ${theme.successColor || baseTheme.successColor};
|
|
40
|
+
$warning: ${theme.warningColor || baseTheme.warningColor};
|
|
41
|
+
$danger: ${theme.dangerColor || baseTheme.dangerColor};
|
|
42
|
+
$themeColor: ${theme.themeColor || baseTheme.themeColor};
|
|
43
|
+
$splashColor: ${theme.splashColor || baseTheme.splashColor};
|
|
44
|
+
|
|
45
|
+
@import "${functionPath}";
|
|
46
|
+
$themeColor-invert: findColorInvert($themeColor);
|
|
47
|
+
$splashColor-invert: findColorInvert($splashColor);
|
|
48
|
+
|
|
49
|
+
@import "${bulmaPath}";
|
|
50
|
+
@import "${checkRadioPath}";
|
|
51
|
+
@import "${bulmaSliderPath}";
|
|
52
|
+
@import "${bulmaSwitchPath}";
|
|
53
|
+
// Syntax: https://sass-lang.com/documentation/breaking-changes/css-vars
|
|
54
|
+
:root {
|
|
55
|
+
--primary-color: #{$primary};
|
|
56
|
+
--link-color: #{$link};
|
|
57
|
+
--success-color: #{$success};
|
|
58
|
+
--info-color: #{$info};
|
|
59
|
+
--warning-color: #{$warning};
|
|
60
|
+
--danger-color: #{$danger};
|
|
61
|
+
--theme-color: #{$themeColor};
|
|
62
|
+
--splash-color: #{$splashColor};
|
|
63
|
+
--primary-color-invert: #{$primary-invert};
|
|
64
|
+
--link-color-invert: #{$link-invert};
|
|
65
|
+
--success-color-invert: #{$success-invert};
|
|
66
|
+
--info-color-invert: #{$info-invert};
|
|
67
|
+
--warning-color-invert: #{$warning-invert};
|
|
68
|
+
--danger-color-invert: #{$danger-invert};
|
|
69
|
+
--theme-color-invert: #{$themeColor-invert};
|
|
70
|
+
--splash-color-invert: #{$splashColor-invert};
|
|
71
|
+
}`;
|
|
72
|
+
}
|
|
73
|
+
export function createBulmaHandler({ createTheme, getTheme }) {
|
|
74
|
+
return async (ctx) => {
|
|
75
|
+
const { query } = ctx;
|
|
76
|
+
const theme = {
|
|
77
|
+
bulmaVersion: bulma.version,
|
|
78
|
+
primaryColor: (getQueryParameter(query.primaryColor) || baseTheme.primaryColor).toLowerCase(),
|
|
79
|
+
linkColor: (getQueryParameter(query.linkColor) || baseTheme.linkColor).toLowerCase(),
|
|
80
|
+
successColor: (getQueryParameter(query.successColor) || baseTheme.successColor).toLowerCase(),
|
|
81
|
+
infoColor: (getQueryParameter(query.infoColor) || baseTheme.infoColor).toLowerCase(),
|
|
82
|
+
warningColor: (getQueryParameter(query.warningColor) || baseTheme.warningColor).toLowerCase(),
|
|
83
|
+
dangerColor: (getQueryParameter(query.dangerColor) || baseTheme.dangerColor).toLowerCase(),
|
|
84
|
+
themeColor: (getQueryParameter(query.themeColor) || baseTheme.themeColor).toLowerCase(),
|
|
85
|
+
splashColor: (getQueryParameter(query.splashColor) || baseTheme.splashColor).toLowerCase(),
|
|
86
|
+
fontFamily: getQueryParameter(query.fontFamily) || baseTheme.font.family,
|
|
87
|
+
fontSource: getQueryParameter(query.fontSource) || baseTheme.font.source,
|
|
88
|
+
};
|
|
89
|
+
const result = await getTheme({ theme });
|
|
90
|
+
let css = result === null || result === void 0 ? void 0 : result.css;
|
|
91
|
+
if (!css) {
|
|
92
|
+
css = stripBom(String(renderSync({
|
|
93
|
+
data: processStyle(theme),
|
|
94
|
+
outputStyle: 'compressed',
|
|
95
|
+
logger: {
|
|
96
|
+
debug(message) {
|
|
97
|
+
logger.silly(message);
|
|
98
|
+
},
|
|
99
|
+
warn(message, { deprecation }) {
|
|
100
|
+
if (!deprecation) {
|
|
101
|
+
logger.verbose(message);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
}).css));
|
|
106
|
+
}
|
|
107
|
+
ctx.body = css;
|
|
108
|
+
ctx.type = 'text/css';
|
|
109
|
+
ctx.set('Cache-Control', 'max-age=31536000,immutable');
|
|
110
|
+
if (!result) {
|
|
111
|
+
// This is not awaited on purpose.
|
|
112
|
+
createTheme({ ...theme, css });
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=bulmaHandler.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { notFound } from '@hapi/boom';
|
|
2
|
+
export function createCssHandler(type, { getAppStyles }) {
|
|
3
|
+
return async (ctx) => {
|
|
4
|
+
const app = await getAppStyles({ context: ctx, query: { attributes: [type], raw: true } });
|
|
5
|
+
if (!app) {
|
|
6
|
+
throw notFound('App not found');
|
|
7
|
+
}
|
|
8
|
+
ctx.body = app[type];
|
|
9
|
+
ctx.type = 'css';
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=cssHandler.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { serveIcon } from '@appsemble/node-utils';
|
|
2
|
+
import { isEqual, parseISO } from 'date-fns';
|
|
3
|
+
export function createIconHandler({ getApp, getAppIcon, getDbUpdated }) {
|
|
4
|
+
return async (ctx) => {
|
|
5
|
+
const { params: { size = 128 }, query: { maskable = false, updated }, } = ctx;
|
|
6
|
+
const app = await getApp({
|
|
7
|
+
context: ctx,
|
|
8
|
+
query: { where: { ...(updated ? { updated } : {}) } },
|
|
9
|
+
});
|
|
10
|
+
const dbUpdated = app ? await getDbUpdated({ app, maskable, context: ctx }) : false;
|
|
11
|
+
const appIcon = app ? await getAppIcon({ app, context: ctx }) : null;
|
|
12
|
+
await serveIcon(ctx, {
|
|
13
|
+
background: maskable ? app.iconBackground || '#ffffff' : undefined,
|
|
14
|
+
cache: dbUpdated ? isEqual(parseISO(updated), dbUpdated) : false,
|
|
15
|
+
fallback: 'mobile-alt-solid.png',
|
|
16
|
+
height: size && Number.parseInt(size),
|
|
17
|
+
icon: appIcon,
|
|
18
|
+
maskable: Boolean(maskable),
|
|
19
|
+
width: size && Number.parseInt(size),
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=iconHandler.js.map
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
import { tinyRouter } from '@appsemble/node-utils';
|
|
4
|
+
import { noop, partialNormalized, partialSemver } from '@appsemble/utils';
|
|
5
|
+
import faPkg from '@fortawesome/fontawesome-free/package.json' assert { type: 'json' };
|
|
6
|
+
import mount from 'koa-mount';
|
|
7
|
+
import serve from 'koa-static';
|
|
8
|
+
import { createBlockAssetHandler } from './blockAssetHandler.js';
|
|
9
|
+
import { createBlockCssHandler } from './blockCssHandler.js';
|
|
10
|
+
import { createBulmaHandler } from './bulmaHandler.js';
|
|
11
|
+
import { createCssHandler } from './cssHandler.js';
|
|
12
|
+
import { createIconHandler } from './iconHandler.js';
|
|
13
|
+
import { createIndexHandler } from './indexHandler.js';
|
|
14
|
+
import { createManifestHandler } from './manifestHandler.js';
|
|
15
|
+
import { createRobotsHandler } from './robotsHandler.js';
|
|
16
|
+
import { createScreenshotHandler } from './screenshotHandler.js';
|
|
17
|
+
import { createServiceWorkerHandler } from './serviceWorkerHandler.js';
|
|
18
|
+
import { createStaticHandler } from './staticHandler.js';
|
|
19
|
+
const require = createRequire(import.meta.url);
|
|
20
|
+
const blockName = `(?<name>@${partialNormalized.source}/${partialNormalized.source})`;
|
|
21
|
+
export function createAppRouter(options) {
|
|
22
|
+
return tinyRouter([
|
|
23
|
+
{
|
|
24
|
+
route: '/manifest.json',
|
|
25
|
+
get: createManifestHandler(options),
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
route: '/robots.txt',
|
|
29
|
+
get: createRobotsHandler(),
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
route: '/service-worker.js',
|
|
33
|
+
get: createServiceWorkerHandler(options),
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
route: /^\/icon-(?<size>\d+)\.png$/,
|
|
37
|
+
get: createIconHandler(options),
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
route: /^\/screenshots\/(?<id>\d+)\.(?<ext>[a-z]+)$/,
|
|
41
|
+
get: createScreenshotHandler(options),
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
route: new RegExp(`^/api/blocks/${blockName}/versions/(?<version>${partialSemver.source})/(?<filename>.+)$`),
|
|
45
|
+
get: createBlockAssetHandler(options),
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
route: /^\/bulma/,
|
|
49
|
+
get: createBulmaHandler(options),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
route: /^\/fa\//,
|
|
53
|
+
get: mount(`/fa/${faPkg.version}`, serve(dirname(require.resolve('@fortawesome/fontawesome-free/package.json')))),
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
route: '/core.css',
|
|
57
|
+
get: createCssHandler('coreStyle', options),
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
route: '/shared.css',
|
|
61
|
+
get: createCssHandler('sharedStyle', options),
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
route: new RegExp(`^/${blockName}\\.css`),
|
|
65
|
+
get: createBlockCssHandler(options),
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
route: /(^|\/)\.well-known(\/|$)/,
|
|
69
|
+
any: noop,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
route: '/index.html',
|
|
73
|
+
get: createIndexHandler(options),
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
route: /\.[a-z]\w*$/i,
|
|
77
|
+
any: createStaticHandler('app'),
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
route: /.*/,
|
|
81
|
+
get: createIndexHandler(options),
|
|
82
|
+
},
|
|
83
|
+
]);
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type Middleware } from 'koa';
|
|
2
|
+
import { type Options } from '../../types.js';
|
|
3
|
+
export declare const bulmaURL: string;
|
|
4
|
+
export declare const faURL: string;
|
|
5
|
+
export declare function createIndexHandler({ createSettings, getApp, getAppDetails, getAppMessages, getAppUrl, getCsp, getHost, }: Options): Middleware;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { createThemeURL, defaultLocale, getAppBlocks, mergeThemes } from '@appsemble/utils';
|
|
3
|
+
import faPkg from '@fortawesome/fontawesome-free/package.json' assert { type: 'json' };
|
|
4
|
+
import bulmaPkg from 'bulma/package.json' assert { type: 'json' };
|
|
5
|
+
import { organizationBlocklist } from '../../../organizationBlocklist.js';
|
|
6
|
+
import { makeCSP, render } from '../../../render.js';
|
|
7
|
+
export const bulmaURL = `/bulma/${bulmaPkg.version}/bulma.min.css`;
|
|
8
|
+
export const faURL = `/fa/${faPkg.version}/css/all.min.css`;
|
|
9
|
+
export function createIndexHandler({ createSettings, getApp, getAppDetails, getAppMessages, getAppUrl, getCsp, getHost, }) {
|
|
10
|
+
return async (ctx) => {
|
|
11
|
+
var _a;
|
|
12
|
+
const { hostname, path } = ctx;
|
|
13
|
+
const host = getHost({ context: ctx });
|
|
14
|
+
const app = await getApp({ context: ctx });
|
|
15
|
+
if (!app) {
|
|
16
|
+
const { appPath, organizationId } = await getAppDetails({ context: ctx });
|
|
17
|
+
if (organizationId && !appPath) {
|
|
18
|
+
return ctx.redirect(organizationBlocklist.includes(organizationId)
|
|
19
|
+
? host
|
|
20
|
+
: String(new URL(`/organizations/${organizationId}`, host)));
|
|
21
|
+
}
|
|
22
|
+
ctx.status = 404;
|
|
23
|
+
return render(ctx, 'app/error.html', {
|
|
24
|
+
bulmaURL,
|
|
25
|
+
faURL,
|
|
26
|
+
message: 'The app you are looking for could not be found.',
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
const appUrl = await getAppUrl({ app, context: ctx });
|
|
30
|
+
if (appUrl.hostname !== hostname) {
|
|
31
|
+
appUrl.pathname = path;
|
|
32
|
+
appUrl.search = ctx.querystring;
|
|
33
|
+
ctx.redirect(String(appUrl));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const defaultLanguage = app.definition.defaultLanguage || defaultLocale;
|
|
37
|
+
const appMessages = await getAppMessages({ app, context: ctx });
|
|
38
|
+
const languages = [
|
|
39
|
+
...new Set([...appMessages.map(({ language }) => language), defaultLanguage]),
|
|
40
|
+
].sort();
|
|
41
|
+
const identifiableBlocks = getAppBlocks(app.definition);
|
|
42
|
+
const [settingsHash, settings] = await createSettings({
|
|
43
|
+
context: ctx,
|
|
44
|
+
app,
|
|
45
|
+
host,
|
|
46
|
+
hostname,
|
|
47
|
+
identifiableBlocks,
|
|
48
|
+
languages,
|
|
49
|
+
});
|
|
50
|
+
const nonce = randomBytes(16).toString('base64');
|
|
51
|
+
const csp = getCsp({ app, settingsHash, hostname, host, nonce });
|
|
52
|
+
ctx.set('Content-Security-Policy', makeCSP(csp));
|
|
53
|
+
const updated = app.$updated ? new Date(app.$updated) : new Date();
|
|
54
|
+
return render(ctx, 'app/index.html', {
|
|
55
|
+
app,
|
|
56
|
+
appUrl: String(appUrl),
|
|
57
|
+
host,
|
|
58
|
+
locale: defaultLanguage,
|
|
59
|
+
locales: languages.filter((lang) => lang !== defaultLanguage),
|
|
60
|
+
bulmaURL: createThemeURL(mergeThemes(app.definition.theme)),
|
|
61
|
+
faURL,
|
|
62
|
+
nonce,
|
|
63
|
+
settings,
|
|
64
|
+
themeColor: ((_a = app.definition.theme) === null || _a === void 0 ? void 0 : _a.themeColor) || '#ffffff',
|
|
65
|
+
appUpdated: updated.toISOString(),
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=indexHandler.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { baseTheme, normalize } from '@appsemble/utils';
|
|
2
|
+
import { notFound } from '@hapi/boom';
|
|
3
|
+
import { extension } from 'mime-types';
|
|
4
|
+
const iconSizes = [48, 144, 192, 512];
|
|
5
|
+
export function createManifestHandler({ getApp, getAppScreenshots }) {
|
|
6
|
+
return async (ctx) => {
|
|
7
|
+
const app = await getApp({ context: ctx });
|
|
8
|
+
if (!app) {
|
|
9
|
+
throw notFound('App not found');
|
|
10
|
+
}
|
|
11
|
+
const { defaultPage, description, name, theme = baseTheme } = app.definition;
|
|
12
|
+
const { themeColor = '#ffffff', splashColor = themeColor } = theme;
|
|
13
|
+
const appScreenshots = await getAppScreenshots({ app, context: ctx });
|
|
14
|
+
ctx.body = {
|
|
15
|
+
background_color: splashColor,
|
|
16
|
+
description,
|
|
17
|
+
display: 'standalone',
|
|
18
|
+
icons: iconSizes.flatMap((size) => [
|
|
19
|
+
{
|
|
20
|
+
src: `/icon-${size}.png`,
|
|
21
|
+
type: 'image/png',
|
|
22
|
+
sizes: `${size}x${size}`,
|
|
23
|
+
purpose: 'any',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
src: `/icon-${size}.png?maskable=true`,
|
|
27
|
+
type: 'image/png',
|
|
28
|
+
sizes: `${size}x${size}`,
|
|
29
|
+
purpose: 'maskable',
|
|
30
|
+
},
|
|
31
|
+
]),
|
|
32
|
+
name,
|
|
33
|
+
orientation: 'any',
|
|
34
|
+
scope: '/',
|
|
35
|
+
screenshots: appScreenshots.map(({ height, id, mime, width }) => ({
|
|
36
|
+
sizes: `${width}x${height}`,
|
|
37
|
+
src: `/screenshots/${id}.${extension(mime)}`,
|
|
38
|
+
type: mime,
|
|
39
|
+
})),
|
|
40
|
+
short_name: name,
|
|
41
|
+
start_url: `/${normalize(defaultPage)}`,
|
|
42
|
+
theme_color: themeColor,
|
|
43
|
+
};
|
|
44
|
+
ctx.type = 'application/manifest+json';
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=manifestHandler.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { notFound } from '@hapi/boom';
|
|
2
|
+
export function createScreenshotHandler({ getApp, getAppScreenshots }) {
|
|
3
|
+
return async (ctx) => {
|
|
4
|
+
const { id } = ctx.params;
|
|
5
|
+
const app = await getApp({ context: ctx });
|
|
6
|
+
if (!app) {
|
|
7
|
+
throw notFound('App not found');
|
|
8
|
+
}
|
|
9
|
+
const appScreenshots = await getAppScreenshots({ app, context: ctx });
|
|
10
|
+
const appScreenshot = appScreenshots.find((screenshot) => screenshot.id === Number.parseInt(id));
|
|
11
|
+
if (!appScreenshot) {
|
|
12
|
+
throw notFound('Screenshot not found');
|
|
13
|
+
}
|
|
14
|
+
const { mime, screenshot } = appScreenshot;
|
|
15
|
+
ctx.type = mime;
|
|
16
|
+
ctx.body = screenshot;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=screenshotHandler.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { getAppBlocks } from '@appsemble/utils';
|
|
3
|
+
export function createServiceWorkerHandler({ getApp, getBlocksAssetsPaths }) {
|
|
4
|
+
return async (ctx) => {
|
|
5
|
+
const production = process.env.NODE_ENV === 'production';
|
|
6
|
+
const filename = production ? '/service-worker.js' : '/app/service-worker.js';
|
|
7
|
+
const serviceWorker = await (production
|
|
8
|
+
? readFile(new URL('../../../../../dist/app/service-worker.js', import.meta.url), 'utf8')
|
|
9
|
+
: ctx.fs.promises.readFile(filename, 'utf8'));
|
|
10
|
+
const app = await getApp({ context: ctx });
|
|
11
|
+
ctx.assert(app, 404, 'App does not exist.');
|
|
12
|
+
const identifiableBlocks = getAppBlocks(app.definition);
|
|
13
|
+
const blocksAssetsPaths = await getBlocksAssetsPaths({ identifiableBlocks, context: ctx });
|
|
14
|
+
ctx.body = `const blockAssets=${JSON.stringify(blocksAssetsPaths)};${serviceWorker}`;
|
|
15
|
+
ctx.type = 'application/javascript';
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=serviceWorkerHandler.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import serve from 'koa-static';
|
|
3
|
+
export function createStaticHandler(name) {
|
|
4
|
+
return serve(fileURLToPath(new URL(`../../../../../dist/${name}`, import.meta.url)), {
|
|
5
|
+
index: false,
|
|
6
|
+
maxAge: 365 * 24 * 60 * 60 * 1000,
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=staticHandler.js.map
|