@appsemble/node-utils 0.20.45 → 0.21.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 +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.js +1 -1
- 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 +1 -1
- 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 +45 -5
- 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 +118 -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,58 @@
|
|
|
1
|
+
import { notFound, unauthorized } from '@hapi/boom';
|
|
2
|
+
import { request, setTestApp } from 'axios-test-instance';
|
|
3
|
+
import Koa from 'koa';
|
|
4
|
+
import { boomMiddleware } from './boom.js';
|
|
5
|
+
describe('boomMiddleware', () => {
|
|
6
|
+
let app;
|
|
7
|
+
beforeEach(async () => {
|
|
8
|
+
app = new Koa();
|
|
9
|
+
app.use(boomMiddleware());
|
|
10
|
+
await setTestApp(app);
|
|
11
|
+
});
|
|
12
|
+
it('should catch boom errors', async () => {
|
|
13
|
+
app.use(() => {
|
|
14
|
+
throw notFound('Error not found', "It's nowhere to be seen!");
|
|
15
|
+
});
|
|
16
|
+
const response = await request.get('/');
|
|
17
|
+
expect(response).toMatchObject({
|
|
18
|
+
status: 404,
|
|
19
|
+
data: {
|
|
20
|
+
statusCode: 404,
|
|
21
|
+
error: 'Not Found',
|
|
22
|
+
message: 'Error not found',
|
|
23
|
+
data: "It's nowhere to be seen!",
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
it('should rethrow non-boom errors', async () => {
|
|
28
|
+
app.silent = true;
|
|
29
|
+
const error = new Error('This is a test error');
|
|
30
|
+
let result;
|
|
31
|
+
app.use(() => {
|
|
32
|
+
throw error;
|
|
33
|
+
});
|
|
34
|
+
app.on('error', (err) => {
|
|
35
|
+
result = err;
|
|
36
|
+
});
|
|
37
|
+
await request.get('/');
|
|
38
|
+
expect(result).toBe(error);
|
|
39
|
+
});
|
|
40
|
+
it('should set Koa headers correctly', async () => {
|
|
41
|
+
app.use(() => {
|
|
42
|
+
throw unauthorized('Not authorized!', ['Basic realm="Access to test data", charset="UTF-8"']);
|
|
43
|
+
});
|
|
44
|
+
const response = await request.get('/');
|
|
45
|
+
expect(response).toMatchObject({
|
|
46
|
+
status: 401,
|
|
47
|
+
headers: expect.objectContaining({
|
|
48
|
+
'www-authenticate': 'Basic realm="Access to test data", charset="UTF-8"',
|
|
49
|
+
}),
|
|
50
|
+
data: {
|
|
51
|
+
statusCode: 401,
|
|
52
|
+
error: 'Unauthorized',
|
|
53
|
+
message: 'Not authorized!',
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
//# sourceMappingURL=boom.test.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { isIP } from 'node:net';
|
|
2
|
+
import expressToKoa from 'express-to-koa';
|
|
3
|
+
import compose from 'koa-compose';
|
|
4
|
+
import webpack from 'webpack';
|
|
5
|
+
import webpackDevMiddleware from 'webpack-dev-middleware';
|
|
6
|
+
/**
|
|
7
|
+
* Bypass the dev server for API requests to speed them up.
|
|
8
|
+
*/
|
|
9
|
+
const skipRoute = /^\/(api|oauth2\/token)/;
|
|
10
|
+
export async function frontend(webpackConfigs, argv, serveStudio = false) {
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
|
|
12
|
+
// @ts-ignore Because the webpack core config isn’t built when building the server, an error is
|
|
13
|
+
// expected here at build time, but while type checking.
|
|
14
|
+
const { createAppConfig, createStudioConfig } = await import('@appsemble/webpack-core');
|
|
15
|
+
const configApp = createAppConfig({ mode: 'development' });
|
|
16
|
+
const configStudio = createStudioConfig({ mode: 'development' });
|
|
17
|
+
const configs = [configApp, ...(serveStudio ? [configStudio] : []), ...webpackConfigs];
|
|
18
|
+
const compiler = webpack(configs);
|
|
19
|
+
const devMiddleware = webpackDevMiddleware(compiler, { serverSideRender: true });
|
|
20
|
+
// @ts-expect-error outputFileSystem exists despite what the types say.
|
|
21
|
+
const fs = devMiddleware.context.outputFileSystem;
|
|
22
|
+
const koaDevMiddleware = expressToKoa(devMiddleware);
|
|
23
|
+
return compose([
|
|
24
|
+
(ctx, next) => {
|
|
25
|
+
ctx.fs = fs;
|
|
26
|
+
return next();
|
|
27
|
+
},
|
|
28
|
+
(ctx, next) => {
|
|
29
|
+
const { hostname } = ctx;
|
|
30
|
+
if (!skipRoute.test(ctx.path)) {
|
|
31
|
+
return koaDevMiddleware(ctx, next);
|
|
32
|
+
}
|
|
33
|
+
if (new URL(argv.host).hostname === hostname || isIP(hostname)) {
|
|
34
|
+
return next();
|
|
35
|
+
}
|
|
36
|
+
return koaDevMiddleware(ctx, next);
|
|
37
|
+
},
|
|
38
|
+
]);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=frontend.js.map
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
+
import { logger, loggerMiddleware } from '@appsemble/node-utils';
|
|
1
2
|
import { request, setTestApp } from 'axios-test-instance';
|
|
2
3
|
import chalk from 'chalk';
|
|
3
4
|
import Koa from 'koa';
|
|
4
|
-
import { logger } from './logger.js';
|
|
5
|
-
import { loggerMiddleware } from './loggerMiddleware.js';
|
|
6
5
|
class TestError extends Error {
|
|
7
6
|
constructor(message) {
|
|
8
7
|
super(message);
|
|
@@ -11,9 +10,9 @@ class TestError extends Error {
|
|
|
11
10
|
}
|
|
12
11
|
let app;
|
|
13
12
|
beforeEach(async () => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
vi.spyOn(logger, 'info').mockImplementation(() => logger);
|
|
14
|
+
vi.spyOn(logger, 'log').mockImplementation(() => logger);
|
|
15
|
+
vi.useFakeTimers();
|
|
17
16
|
app = new Koa();
|
|
18
17
|
app.use(async (ctx, next) => {
|
|
19
18
|
Object.defineProperty(ctx.request, 'origin', {
|
|
@@ -40,7 +39,7 @@ it('should log requests', async () => {
|
|
|
40
39
|
});
|
|
41
40
|
it('should log success responses as info', async () => {
|
|
42
41
|
app.use((ctx) => {
|
|
43
|
-
|
|
42
|
+
vi.advanceTimersByTime(1);
|
|
44
43
|
ctx.status = 200;
|
|
45
44
|
});
|
|
46
45
|
await request.get('/fries');
|
|
@@ -48,7 +47,7 @@ it('should log success responses as info', async () => {
|
|
|
48
47
|
});
|
|
49
48
|
it('should log redirect responses as info', async () => {
|
|
50
49
|
app.use((ctx) => {
|
|
51
|
-
|
|
50
|
+
vi.advanceTimersByTime(33);
|
|
52
51
|
ctx.redirect('/');
|
|
53
52
|
});
|
|
54
53
|
await request.get('/fries');
|
|
@@ -56,7 +55,7 @@ it('should log redirect responses as info', async () => {
|
|
|
56
55
|
});
|
|
57
56
|
it('should log bad responses as warn', async () => {
|
|
58
57
|
app.use((ctx) => {
|
|
59
|
-
|
|
58
|
+
vi.advanceTimersByTime(3);
|
|
60
59
|
ctx.status = 400;
|
|
61
60
|
});
|
|
62
61
|
await request.get('/burrito');
|
|
@@ -64,7 +63,7 @@ it('should log bad responses as warn', async () => {
|
|
|
64
63
|
});
|
|
65
64
|
it('should log error responses as error', async () => {
|
|
66
65
|
app.use((ctx) => {
|
|
67
|
-
|
|
66
|
+
vi.advanceTimersByTime(53);
|
|
68
67
|
ctx.status = 503;
|
|
69
68
|
});
|
|
70
69
|
await request.get('/wrap');
|
|
@@ -72,7 +71,7 @@ it('should log error responses as error', async () => {
|
|
|
72
71
|
});
|
|
73
72
|
it('should log long request lengths yellow', async () => {
|
|
74
73
|
app.use((ctx) => {
|
|
75
|
-
|
|
74
|
+
vi.advanceTimersByTime(400);
|
|
76
75
|
ctx.status = 200;
|
|
77
76
|
});
|
|
78
77
|
await request.get('/banana');
|
|
@@ -80,19 +79,19 @@ it('should log long request lengths yellow', async () => {
|
|
|
80
79
|
});
|
|
81
80
|
it('should log extremely long request lengths red', async () => {
|
|
82
81
|
app.use((ctx) => {
|
|
83
|
-
|
|
82
|
+
vi.advanceTimersByTime(1337);
|
|
84
83
|
ctx.status = 200;
|
|
85
84
|
});
|
|
86
85
|
await request.get('/pepperoni');
|
|
87
86
|
expect(logger.log).toHaveBeenCalledWith('info', `${chalk.bold('GET')} https://example.com:1337/pepperoni ${chalk.green('200 OK')} ${chalk.red('1337ms')}`);
|
|
88
87
|
});
|
|
89
88
|
it('should log errors as internal server errors and rethrow', async () => {
|
|
90
|
-
const spy =
|
|
89
|
+
const spy = vi.fn();
|
|
91
90
|
const error = new Error('fail');
|
|
92
91
|
let context;
|
|
93
92
|
app.on('error', spy);
|
|
94
93
|
app.use((ctx) => {
|
|
95
|
-
|
|
94
|
+
vi.advanceTimersByTime(86);
|
|
96
95
|
context = ctx;
|
|
97
96
|
throw error;
|
|
98
97
|
});
|
|
@@ -102,7 +101,7 @@ it('should log errors as internal server errors and rethrow', async () => {
|
|
|
102
101
|
});
|
|
103
102
|
it('should append the response length if it is defined', async () => {
|
|
104
103
|
app.use((ctx) => {
|
|
105
|
-
|
|
104
|
+
vi.advanceTimersByTime(1);
|
|
106
105
|
ctx.status = 200;
|
|
107
106
|
ctx.body = '{}';
|
|
108
107
|
});
|
|
@@ -111,7 +110,7 @@ it('should append the response length if it is defined', async () => {
|
|
|
111
110
|
});
|
|
112
111
|
it('should log handled errors correctly', async () => {
|
|
113
112
|
app.use(() => {
|
|
114
|
-
|
|
113
|
+
vi.advanceTimersByTime(15);
|
|
115
114
|
throw new TestError();
|
|
116
115
|
});
|
|
117
116
|
await request.get('potatoes');
|
package/odata.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type Token } from '@odata/parser';
|
|
2
|
+
export declare enum Edm {
|
|
3
|
+
null = "null",
|
|
4
|
+
Boolean = "Edm.Boolean",
|
|
5
|
+
Byte = "Edm.Byte",
|
|
6
|
+
Date = "Edm.Date",
|
|
7
|
+
DateTimeOffset = "Edm.DateTimeOffset",
|
|
8
|
+
Decimal = "Edm.Decimal",
|
|
9
|
+
Double = "Edm.Double",
|
|
10
|
+
Guid = "Edm.Guid",
|
|
11
|
+
Int16 = "Edm.Int16",
|
|
12
|
+
Int32 = "Edm.Int32",
|
|
13
|
+
Int64 = "Edm.Int64",
|
|
14
|
+
SByte = "Edm.SByte",
|
|
15
|
+
Single = "Edm.Single",
|
|
16
|
+
String = "Edm.String"
|
|
17
|
+
}
|
|
18
|
+
export declare function processLiteral(token: Token): Date | boolean | number | string;
|
package/odata.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export var Edm;
|
|
2
|
+
(function (Edm) {
|
|
3
|
+
Edm["null"] = "null";
|
|
4
|
+
Edm["Boolean"] = "Edm.Boolean";
|
|
5
|
+
Edm["Byte"] = "Edm.Byte";
|
|
6
|
+
Edm["Date"] = "Edm.Date";
|
|
7
|
+
Edm["DateTimeOffset"] = "Edm.DateTimeOffset";
|
|
8
|
+
Edm["Decimal"] = "Edm.Decimal";
|
|
9
|
+
Edm["Double"] = "Edm.Double";
|
|
10
|
+
Edm["Guid"] = "Edm.Guid";
|
|
11
|
+
Edm["Int16"] = "Edm.Int16";
|
|
12
|
+
Edm["Int32"] = "Edm.Int32";
|
|
13
|
+
Edm["Int64"] = "Edm.Int64";
|
|
14
|
+
Edm["SByte"] = "Edm.SByte";
|
|
15
|
+
Edm["Single"] = "Edm.Single";
|
|
16
|
+
Edm["String"] = "Edm.String";
|
|
17
|
+
})(Edm || (Edm = {}));
|
|
18
|
+
export function processLiteral(token) {
|
|
19
|
+
switch (token.value) {
|
|
20
|
+
case Edm.Boolean:
|
|
21
|
+
return token.raw === 'true';
|
|
22
|
+
case Edm.String:
|
|
23
|
+
return JSON.parse(`"${token.raw.slice(1, -1).replaceAll('"', '\\"')}"`);
|
|
24
|
+
case Edm.Byte:
|
|
25
|
+
case Edm.Decimal:
|
|
26
|
+
case Edm.Double:
|
|
27
|
+
case Edm.Int16:
|
|
28
|
+
case Edm.Int32:
|
|
29
|
+
case Edm.Int64:
|
|
30
|
+
case Edm.SByte:
|
|
31
|
+
case Edm.Single:
|
|
32
|
+
return Number(token.raw);
|
|
33
|
+
case Edm.Date:
|
|
34
|
+
case Edm.DateTimeOffset:
|
|
35
|
+
// The Date constructor will convert it to UTC.
|
|
36
|
+
return new Date(token.raw);
|
|
37
|
+
case Edm.Guid:
|
|
38
|
+
return token.raw;
|
|
39
|
+
case Edm.null:
|
|
40
|
+
return null;
|
|
41
|
+
default:
|
|
42
|
+
throw new TypeError(`${token.position}: Unhandled OData literal type: ${token.value}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=odata.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appsemble/node-utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.1",
|
|
4
4
|
"description": "NodeJS utilities used by Appsemble internally.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"app",
|
|
@@ -28,31 +28,71 @@
|
|
|
28
28
|
],
|
|
29
29
|
"scripts": {
|
|
30
30
|
"prepack": "tsc --noEmit false",
|
|
31
|
-
"test": "
|
|
31
|
+
"test": "vitest"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@appsemble/
|
|
34
|
+
"@appsemble/types": "0.21.1",
|
|
35
|
+
"@appsemble/utils": "0.21.1",
|
|
36
|
+
"@formatjs/fast-memoize": "^2.0.0",
|
|
37
|
+
"@fortawesome/fontawesome-free": "^6.0.0",
|
|
38
|
+
"@hapi/boom": "^10.0.0",
|
|
39
|
+
"@koa/cors": "^4.0.0",
|
|
40
|
+
"@odata/parser": "^0.2.0",
|
|
35
41
|
"@types/koa": "^2.0.0",
|
|
36
42
|
"axios": "^1.0.0",
|
|
43
|
+
"bulma": "0.9.3",
|
|
44
|
+
"bulma-checkradio": "2.1.1",
|
|
45
|
+
"bulma-slider": "2.0.4",
|
|
46
|
+
"bulma-switch": "^2.0.0",
|
|
37
47
|
"chalk": "^4.0.0",
|
|
38
48
|
"cli-highlight": "^2.0.0",
|
|
49
|
+
"csv-parse": "^5.0.0",
|
|
50
|
+
"date-fns": "^2.0.0",
|
|
51
|
+
"express-to-koa": "^2.0.0",
|
|
39
52
|
"fast-glob": "^3.0.0",
|
|
40
53
|
"form-data": "^4.0.0",
|
|
54
|
+
"intl-messageformat": "^10.0.0",
|
|
55
|
+
"jsonschema": "^1.0.0",
|
|
41
56
|
"koa": "^2.0.0",
|
|
42
57
|
"koa-compose": "^4.0.0",
|
|
58
|
+
"koa-compress": "^5.0.0",
|
|
59
|
+
"koa-mount": "^4.0.0",
|
|
60
|
+
"koa-range": "^0.3.0",
|
|
61
|
+
"koa-static": "^5.0.0",
|
|
62
|
+
"koas-body-parser": "^0.7.0",
|
|
63
|
+
"koas-core": "^0.7.0",
|
|
64
|
+
"koas-operations": "^0.7.0",
|
|
65
|
+
"koas-parameters": "^0.7.0",
|
|
66
|
+
"koas-security": "^0.7.0",
|
|
67
|
+
"koas-serializer": "^0.7.0",
|
|
68
|
+
"koas-status-code": "^0.7.0",
|
|
69
|
+
"koas-swagger-ui": "^0.7.0",
|
|
70
|
+
"language-tags": "^1.0.0",
|
|
71
|
+
"lodash-es": "^4.0.0",
|
|
43
72
|
"logform": "^2.0.0",
|
|
73
|
+
"mime-types": "^2.0.0",
|
|
74
|
+
"mustache": "^4.0.0",
|
|
75
|
+
"parse-duration": "^1.0.0",
|
|
44
76
|
"parse-json": "^7.0.0",
|
|
77
|
+
"sass": "^1.0.0",
|
|
78
|
+
"sharp": "^0.32.0",
|
|
45
79
|
"sort-keys": "^5.0.0",
|
|
46
|
-
"
|
|
80
|
+
"strip-bom": "^5.0.0",
|
|
81
|
+
"type-fest": "^4.0.0",
|
|
82
|
+
"webpack": "^5.0.0",
|
|
83
|
+
"webpack-dev-middleware": "^6.0.0",
|
|
47
84
|
"winston": "^3.0.0",
|
|
48
85
|
"yaml": "^2.0.0",
|
|
49
86
|
"yargs": "^17.0.0"
|
|
50
87
|
},
|
|
51
88
|
"devDependencies": {
|
|
52
89
|
"@sinonjs/fake-timers": "^10.0.0",
|
|
90
|
+
"@types/koa-compress": "^4.0.0",
|
|
91
|
+
"@types/koa-range": "^0.3.0",
|
|
53
92
|
"@types/sinonjs__fake-timers": "^8.0.0",
|
|
54
93
|
"axios-mock-adapter": "^1.0.0",
|
|
55
|
-
"axios-test-instance": "^7.0.0"
|
|
94
|
+
"axios-test-instance": "^7.0.0",
|
|
95
|
+
"vitest": "^0.33.0"
|
|
56
96
|
},
|
|
57
97
|
"peerDependencies": {
|
|
58
98
|
"prettier": "^2"
|
package/parsers.d.ts
ADDED
package/parsers.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { parse } from 'node:querystring';
|
|
2
|
+
import { parse as parseCSV } from 'csv-parse';
|
|
3
|
+
import { bufferParser } from 'koas-body-parser';
|
|
4
|
+
export const xWwwFormUrlencodedParser = async (body, mediaTypeObject, options, ctx) => {
|
|
5
|
+
const buffer = await bufferParser(body, mediaTypeObject, options, ctx);
|
|
6
|
+
return parse(String(buffer));
|
|
7
|
+
};
|
|
8
|
+
export const csvParser = (body) => new Promise((resolve, reject) => {
|
|
9
|
+
body.pipe(parseCSV({ bom: true, columns: true }, (error, records) => {
|
|
10
|
+
if (error) {
|
|
11
|
+
reject(error);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
resolve(records);
|
|
15
|
+
}
|
|
16
|
+
}));
|
|
17
|
+
});
|
|
18
|
+
//# sourceMappingURL=parsers.js.map
|
package/readAsset.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
3
|
+
export declare const assetDir: import("url").URL;
|
|
4
|
+
/**
|
|
5
|
+
* Read a file from the server assets directory.
|
|
6
|
+
*
|
|
7
|
+
* @param filename The path to the file to read, relative to the assets directory.
|
|
8
|
+
*
|
|
9
|
+
* param encoding If specified, decode the file using this encoding.
|
|
10
|
+
* @returns The content of the specified file.
|
|
11
|
+
*/
|
|
12
|
+
export declare function readAsset(filename: string): Promise<Buffer>;
|
|
13
|
+
export declare function readAsset(filename: string, encoding: BufferEncoding): Promise<string>;
|
package/readAsset.js
ADDED
package/readFileOrString.d.ts
CHANGED
package/render.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ContentSecurityPolicy } from '@appsemble/node-utils';
|
|
2
|
+
import { type Context } from 'koa';
|
|
3
|
+
/**
|
|
4
|
+
* Render settings as an HTML script tag.
|
|
5
|
+
*
|
|
6
|
+
* @param settings The settings to render. This must be a JSON serializable object.
|
|
7
|
+
* @param statements Custom JavaScript statements to append.
|
|
8
|
+
* @returns A tuple of the digest and the HTML script tag. The digest should be added to the CSP
|
|
9
|
+
* `script-src`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createSettings(settings: unknown, statements?: string[]): [digest: string, script: string];
|
|
12
|
+
export declare function render(ctx: Context, filename: string, data: Record<string, unknown>): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Convert a CSP key / values pair object into a real content security policy string.
|
|
15
|
+
*
|
|
16
|
+
* Any falsy values will be excluded.
|
|
17
|
+
*
|
|
18
|
+
* @param csp The CSP object to convert
|
|
19
|
+
* @returns The CSP object as a string
|
|
20
|
+
*/
|
|
21
|
+
export declare function makeCSP(csp: ContentSecurityPolicy): string;
|
package/render.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { compareStrings } from '@appsemble/utils';
|
|
4
|
+
import mustache from 'mustache';
|
|
5
|
+
/**
|
|
6
|
+
* Render settings as an HTML script tag.
|
|
7
|
+
*
|
|
8
|
+
* @param settings The settings to render. This must be a JSON serializable object.
|
|
9
|
+
* @param statements Custom JavaScript statements to append.
|
|
10
|
+
* @returns A tuple of the digest and the HTML script tag. The digest should be added to the CSP
|
|
11
|
+
* `script-src`.
|
|
12
|
+
*/
|
|
13
|
+
export function createSettings(settings, statements = []) {
|
|
14
|
+
const script = [`window.settings=${JSON.stringify(settings)}`, ...statements].join(';');
|
|
15
|
+
const hash = createHash('sha256').update(script, 'utf8').digest('base64');
|
|
16
|
+
return [`'sha256-${hash}'`, `<script>${script}</script>`];
|
|
17
|
+
}
|
|
18
|
+
export async function render(ctx, filename, data) {
|
|
19
|
+
if (process.env.NODE_ENV === 'test') {
|
|
20
|
+
ctx.body = JSON.stringify({ filename, data });
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
const template = await (process.env.NODE_ENV === 'production'
|
|
24
|
+
? readFile(new URL(`../../dist/${filename}`, import.meta.url), 'utf8')
|
|
25
|
+
: ctx.fs.promises.readFile(`/${filename}`, 'utf8'));
|
|
26
|
+
ctx.body = mustache.render(template, data);
|
|
27
|
+
}
|
|
28
|
+
ctx.type = 'html';
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Convert a CSP key / values pair object into a real content security policy string.
|
|
32
|
+
*
|
|
33
|
+
* Any falsy values will be excluded.
|
|
34
|
+
*
|
|
35
|
+
* @param csp The CSP object to convert
|
|
36
|
+
* @returns The CSP object as a string
|
|
37
|
+
*/
|
|
38
|
+
export function makeCSP(csp) {
|
|
39
|
+
return Object.entries(csp)
|
|
40
|
+
.map(([key, values]) => [key, values.filter(Boolean)])
|
|
41
|
+
.filter(([, values]) => values === null || values === void 0 ? void 0 : values.length)
|
|
42
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
43
|
+
.map(([key, values]) => `${key} ${[...new Set(values)].sort(compareStrings).join(' ')}`)
|
|
44
|
+
.join('; ');
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=render.js.map
|
package/resource.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type PreparedAsset } from '@appsemble/node-utils';
|
|
2
|
+
import { type App, type ResourceDefinition } from '@appsemble/types';
|
|
3
|
+
import { type PreValidatePropertyFunction } from 'jsonschema';
|
|
4
|
+
import { type Context } from 'koa';
|
|
5
|
+
import { type File } from 'koas-body-parser';
|
|
6
|
+
/**
|
|
7
|
+
* Get the resource definition of an app by name.
|
|
8
|
+
*
|
|
9
|
+
* If there is no match, a 404 HTTP error is thrown.
|
|
10
|
+
*
|
|
11
|
+
* @param app The app to get the resource definition of
|
|
12
|
+
* @param resourceType The name of the resource definition to get.
|
|
13
|
+
* @param view The view that’s being used.
|
|
14
|
+
* @returns The matching resource definition.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getResourceDefinition(app: App, resourceType: string, view?: string): ResourceDefinition;
|
|
17
|
+
/**
|
|
18
|
+
* Extracts the IDs of resource request body.
|
|
19
|
+
*
|
|
20
|
+
* @param ctx The Koa context to extract the body from.
|
|
21
|
+
* @returns A tuple which consists of:
|
|
22
|
+
*
|
|
23
|
+
* 1. One or more resources processed from the request body.
|
|
24
|
+
* 2. A list of newly uploaded assets which should be linked to the resources.
|
|
25
|
+
* 3. preValidateProperty function used for reconstructing resources from a CSV file.
|
|
26
|
+
*/
|
|
27
|
+
export declare function extractResourceBody(ctx: Context): [Record<string, unknown> | Record<string, unknown>[], File[], PreValidatePropertyFunction];
|
|
28
|
+
/**
|
|
29
|
+
* Process an incoming resource request body.
|
|
30
|
+
*
|
|
31
|
+
* This handles JSON schema validation, resource expiration, and asset linking and validation.
|
|
32
|
+
*
|
|
33
|
+
* @param ctx The Koa context to process.
|
|
34
|
+
* @param definition The resource definition to use for processing the request body.
|
|
35
|
+
* @param knownAssetIds A list of asset IDs that are already known to be linked to the resource.
|
|
36
|
+
* @param knownExpires A previously known expires value.
|
|
37
|
+
* @returns A tuple which consists of:
|
|
38
|
+
*
|
|
39
|
+
* 1. One or more resources processed from the request body.
|
|
40
|
+
* 2. A list of newly uploaded assets which should be linked to the resources.
|
|
41
|
+
* 3. Asset IDs from the `knownAssetIds` array which are no longer used.
|
|
42
|
+
*/
|
|
43
|
+
export declare function processResourceBody(ctx: Context, definition: ResourceDefinition, knownAssetIds?: string[], knownExpires?: Date): [Record<string, unknown> | Record<string, unknown>[], PreparedAsset[], string[]];
|