@appsemble/utils 0.30.14-test.3 → 0.30.14-test.4
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/api/components/schemas/AppServiceSecret.js +4 -0
- package/appMessages.js +5 -8
- package/assets.js +2 -3
- package/authorization.js +4 -6
- package/examples.js +2 -2
- package/ics.js +2 -3
- package/iterApp.js +6 -11
- package/jsonschema.js +1 -2
- package/package.json +3 -3
- package/remap.js +16 -20
- package/remap.test.js +3 -3
- package/validateAppMessages.js +3 -4
- package/validation.js +30 -37
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
#  Appsemble Utilities
|
|
2
2
|
|
|
3
3
|
> Internal utility functions used across multiple Appsemble projects.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@appsemble/utils)
|
|
6
|
-
[](https://gitlab.com/appsemble/appsemble/-/releases/0.30.14-test.4)
|
|
7
7
|
[](https://prettier.io)
|
|
8
8
|
|
|
9
9
|
## Table of Contents
|
|
@@ -26,5 +26,5 @@ not guaranteed.
|
|
|
26
26
|
|
|
27
27
|
## License
|
|
28
28
|
|
|
29
|
-
[LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.30.14-test.
|
|
29
|
+
[LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.30.14-test.4/LICENSE.md) ©
|
|
30
30
|
[Appsemble](https://appsemble.com)
|
|
@@ -44,6 +44,10 @@ export const AppServiceSecret = {
|
|
|
44
44
|
type: 'string',
|
|
45
45
|
description: 'The scope to request access tokens for.',
|
|
46
46
|
},
|
|
47
|
+
ca: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
description: 'The custom certificate authority.',
|
|
50
|
+
},
|
|
47
51
|
},
|
|
48
52
|
};
|
|
49
53
|
//# sourceMappingURL=AppServiceSecret.js.map
|
package/appMessages.js
CHANGED
|
@@ -7,7 +7,6 @@ import { normalize } from './normalize.js';
|
|
|
7
7
|
* @returns All message IDs found
|
|
8
8
|
*/
|
|
9
9
|
export function findMessageIds(obj) {
|
|
10
|
-
var _a;
|
|
11
10
|
if (!obj || typeof obj !== 'object') {
|
|
12
11
|
return {};
|
|
13
12
|
}
|
|
@@ -18,8 +17,8 @@ export function findMessageIds(obj) {
|
|
|
18
17
|
// Remappers throw if multiple keys are defined, so this means it’s not a remapper.
|
|
19
18
|
if (entries.length === 1) {
|
|
20
19
|
const [[key, value]] = entries;
|
|
21
|
-
if (key === 'string.format' && typeof
|
|
22
|
-
return { [value.messageId]:
|
|
20
|
+
if (key === 'string.format' && typeof value?.messageId === 'string') {
|
|
21
|
+
return { [value.messageId]: value.template ?? '', ...findMessageIds(value.values) };
|
|
23
22
|
}
|
|
24
23
|
if (key === 'translate') {
|
|
25
24
|
return { [value]: '' };
|
|
@@ -36,12 +35,11 @@ export function findMessageIds(obj) {
|
|
|
36
35
|
* @returns A list of message IDs
|
|
37
36
|
*/
|
|
38
37
|
export function extractAppMessages(app, onBlock) {
|
|
39
|
-
var _a, _b;
|
|
40
38
|
const messages = {
|
|
41
39
|
app: {
|
|
42
40
|
name: app.name,
|
|
43
41
|
description: app.description,
|
|
44
|
-
...Object.fromEntries(Object.entries(
|
|
42
|
+
...Object.fromEntries(Object.entries(app.security?.roles ?? {}).flatMap(([role, roleDefinition]) => [
|
|
45
43
|
[`app.roles.${role}`, role],
|
|
46
44
|
[`app.roles.${role}.description`, roleDefinition.description],
|
|
47
45
|
])),
|
|
@@ -90,19 +88,18 @@ export function extractAppMessages(app, onBlock) {
|
|
|
90
88
|
}
|
|
91
89
|
},
|
|
92
90
|
onPage(page) {
|
|
93
|
-
var _a, _b;
|
|
94
91
|
const prefix = `pages.${normalize(page.name)}`;
|
|
95
92
|
messages.app[prefix] = page.name;
|
|
96
93
|
if (page.type === 'tabs' && Array.isArray(page.tabs)) {
|
|
97
94
|
for (const [index, tab] of page.tabs.entries()) {
|
|
98
95
|
messages.app[`${prefix}.tabs.${index}`] =
|
|
99
|
-
(
|
|
96
|
+
(typeof tab.name === 'string' ? tab.name : '') ?? '';
|
|
100
97
|
}
|
|
101
98
|
}
|
|
102
99
|
if (page.type === 'flow') {
|
|
103
100
|
for (const [index, step] of page.steps.entries()) {
|
|
104
101
|
messages.app[`${prefix}.steps.${index}`] =
|
|
105
|
-
(
|
|
102
|
+
(typeof step.name === 'string' ? step.name : '') ?? '';
|
|
106
103
|
}
|
|
107
104
|
}
|
|
108
105
|
if (page.type === 'loop') {
|
package/assets.js
CHANGED
|
@@ -35,12 +35,11 @@ const mimeTypeCategories = {
|
|
|
35
35
|
* @returns A category that the mimetype falls into or null if no category is found
|
|
36
36
|
*/
|
|
37
37
|
export function getMimeTypeCategory(mimeType) {
|
|
38
|
-
var _a;
|
|
39
38
|
const [mimeBaseType, mimeSubType] = mimeType.split('/');
|
|
40
|
-
const [category] =
|
|
39
|
+
const [category] = Object.entries(mimeTypeCategories).find(([, types]) => types.some((type) => {
|
|
41
40
|
const [baseType, subType] = type.split('/');
|
|
42
41
|
return baseType === mimeBaseType && (subType === '*' || subType === mimeSubType);
|
|
43
|
-
}))
|
|
42
|
+
})) ?? [null];
|
|
44
43
|
return category;
|
|
45
44
|
}
|
|
46
45
|
export function getMimeTypeCategories(mimeTypes) {
|
package/authorization.js
CHANGED
|
@@ -26,14 +26,13 @@ function checkAppPermissions(acquiredPermissions, requiredPermissions) {
|
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
export function getAppInheritedRoles(appSecurityDefinition, roles, accumulatedRoles = []) {
|
|
29
|
-
var _a;
|
|
30
29
|
if (!appSecurityDefinition || !roles) {
|
|
31
30
|
return [];
|
|
32
31
|
}
|
|
33
32
|
for (const role of roles) {
|
|
34
33
|
if (!accumulatedRoles.includes(role)) {
|
|
35
34
|
accumulatedRoles.push(role);
|
|
36
|
-
const roleDefinition =
|
|
35
|
+
const roleDefinition = appSecurityDefinition.roles?.[role];
|
|
37
36
|
if (roleDefinition && roleDefinition.inherits) {
|
|
38
37
|
accumulatedRoles.push(...getAppInheritedRoles(appSecurityDefinition, roleDefinition.inherits, accumulatedRoles));
|
|
39
38
|
}
|
|
@@ -42,11 +41,10 @@ export function getAppInheritedRoles(appSecurityDefinition, roles, accumulatedRo
|
|
|
42
41
|
return Array.from(new Set(accumulatedRoles));
|
|
43
42
|
}
|
|
44
43
|
export function getAppRolePermissions(appSecurityDefinition, roles) {
|
|
45
|
-
var _a;
|
|
46
44
|
const accumulatedPermissions = [];
|
|
47
45
|
const inheritedRoles = getAppInheritedRoles(appSecurityDefinition, roles);
|
|
48
46
|
for (const role of inheritedRoles) {
|
|
49
|
-
const roleDefinition =
|
|
47
|
+
const roleDefinition = appSecurityDefinition.roles?.[role];
|
|
50
48
|
if (roleDefinition) {
|
|
51
49
|
const rolePermissions = roleDefinition.permissions;
|
|
52
50
|
if (rolePermissions) {
|
|
@@ -76,7 +74,7 @@ export function checkGuestAppPermissions(appSecurityDefinition, requiredPermissi
|
|
|
76
74
|
return checkAppPermissions(guestPermissions, requiredPermissions);
|
|
77
75
|
}
|
|
78
76
|
export function getAppRoles(appSecurityDefinition) {
|
|
79
|
-
return Array.from(new Set(Object.keys(
|
|
77
|
+
return Array.from(new Set(Object.keys(appSecurityDefinition?.roles || {})));
|
|
80
78
|
}
|
|
81
79
|
export function getAppPossibleGuestPermissions(appDefinition) {
|
|
82
80
|
const possibleAllViews = {};
|
|
@@ -87,7 +85,7 @@ export function getAppPossibleGuestPermissions(appDefinition) {
|
|
|
87
85
|
}
|
|
88
86
|
}
|
|
89
87
|
for (const view of Object.keys(possibleAllViews)) {
|
|
90
|
-
if (resourceDefinitions.some((resourceDefinition) =>
|
|
88
|
+
if (resourceDefinitions.some((resourceDefinition) => !resourceDefinition.views?.[view])) {
|
|
91
89
|
possibleAllViews[view] = false;
|
|
92
90
|
}
|
|
93
91
|
}
|
package/examples.js
CHANGED
|
@@ -691,7 +691,7 @@ export const examples = {
|
|
|
691
691
|
* @returns Example based on the input options.
|
|
692
692
|
*/
|
|
693
693
|
export function schemaExample(remapper, options) {
|
|
694
|
-
const { exclude = [], input = 'inline', result } = options
|
|
694
|
+
const { exclude = [], input = 'inline', result } = options ?? {};
|
|
695
695
|
let example = '';
|
|
696
696
|
if (!exclude.includes('input')) {
|
|
697
697
|
const spacing = input === 'pretty' && 2;
|
|
@@ -713,7 +713,7 @@ export function createExampleContext(url, lang, userInfo, history) {
|
|
|
713
713
|
url: String(url),
|
|
714
714
|
appUrl: `${url.protocol}//example-app.example-organization.${url.host}`,
|
|
715
715
|
context: {},
|
|
716
|
-
history: history
|
|
716
|
+
history: history ?? ['Default example value'],
|
|
717
717
|
appId: 0,
|
|
718
718
|
locale: 'en',
|
|
719
719
|
pageData: { default: 'Page data' },
|
package/ics.js
CHANGED
|
@@ -24,15 +24,14 @@ export function getDuration(duration) {
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
export function processLocation(location) {
|
|
27
|
-
var _a, _b, _c, _d, _e;
|
|
28
27
|
if (typeof location !== 'object' || !location) {
|
|
29
28
|
return;
|
|
30
29
|
}
|
|
31
|
-
const lat =
|
|
30
|
+
const lat = location.latitude ?? location.lat ?? location[0];
|
|
32
31
|
if (typeof lat !== 'number' || !Number.isFinite(lat) || lat < -90 || lat > 90) {
|
|
33
32
|
return;
|
|
34
33
|
}
|
|
35
|
-
let lon =
|
|
34
|
+
let lon = location.longitude ?? location.lon ?? location.lng ?? location[1];
|
|
36
35
|
if (typeof lon !== 'number' || !Number.isFinite(lon)) {
|
|
37
36
|
return;
|
|
38
37
|
}
|
package/iterApp.js
CHANGED
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
* @returns True if any callback returns true, false otherwise.
|
|
10
10
|
*/
|
|
11
11
|
export function iterAction(action, callbacks, prefix = []) {
|
|
12
|
-
|
|
13
|
-
if ((_a = callbacks.onAction) === null || _a === void 0 ? void 0 : _a.call(callbacks, action, prefix)) {
|
|
12
|
+
if (callbacks.onAction?.(action, prefix)) {
|
|
14
13
|
return true;
|
|
15
14
|
}
|
|
16
15
|
if (action.onSuccess && iterAction(action.onSuccess, callbacks, [...prefix, 'onSuccess'])) {
|
|
@@ -33,8 +32,7 @@ export function iterAction(action, callbacks, prefix = []) {
|
|
|
33
32
|
return false;
|
|
34
33
|
}
|
|
35
34
|
export function iterController(controller, callbacks, prefix = []) {
|
|
36
|
-
|
|
37
|
-
if ((_a = callbacks.onController) === null || _a === void 0 ? void 0 : _a.call(callbacks, controller, prefix)) {
|
|
35
|
+
if (callbacks.onController?.(controller, prefix)) {
|
|
38
36
|
return true;
|
|
39
37
|
}
|
|
40
38
|
if (controller.actions) {
|
|
@@ -53,8 +51,7 @@ export function iterController(controller, callbacks, prefix = []) {
|
|
|
53
51
|
* @returns True if any callback returns true, false otherwise.
|
|
54
52
|
*/
|
|
55
53
|
export function iterBlock(block, callbacks, prefix = []) {
|
|
56
|
-
|
|
57
|
-
if ((_a = callbacks.onBlock) === null || _a === void 0 ? void 0 : _a.call(callbacks, block, prefix)) {
|
|
54
|
+
if (callbacks.onBlock?.(block, prefix)) {
|
|
58
55
|
return true;
|
|
59
56
|
}
|
|
60
57
|
if (block.actions) {
|
|
@@ -73,8 +70,7 @@ export function iterBlock(block, callbacks, prefix = []) {
|
|
|
73
70
|
* @returns True if any callback returns true, false otherwise.
|
|
74
71
|
*/
|
|
75
72
|
export function iterBlockList(blockList, callbacks, prefix = []) {
|
|
76
|
-
|
|
77
|
-
if ((_a = callbacks.onBlockList) === null || _a === void 0 ? void 0 : _a.call(callbacks, blockList, prefix)) {
|
|
73
|
+
if (callbacks.onBlockList?.(blockList, prefix)) {
|
|
78
74
|
return true;
|
|
79
75
|
}
|
|
80
76
|
return blockList.some((block, index) => iterBlock(block, callbacks, [...prefix, index]));
|
|
@@ -90,8 +86,7 @@ export function iterBlockList(blockList, callbacks, prefix = []) {
|
|
|
90
86
|
* @returns True if any callback returns true, false otherwise.
|
|
91
87
|
*/
|
|
92
88
|
export function iterPage(page, callbacks, prefix = []) {
|
|
93
|
-
|
|
94
|
-
if ((_a = callbacks.onPage) === null || _a === void 0 ? void 0 : _a.call(callbacks, page, prefix)) {
|
|
89
|
+
if (callbacks.onPage?.(page, prefix)) {
|
|
95
90
|
return true;
|
|
96
91
|
}
|
|
97
92
|
if (page.type === 'flow' || page.type === 'tabs') {
|
|
@@ -145,7 +140,7 @@ export function iterApp(app, callbacks) {
|
|
|
145
140
|
}
|
|
146
141
|
if (app.cron) {
|
|
147
142
|
for (const [name, job] of Object.entries(app.cron)) {
|
|
148
|
-
if (
|
|
143
|
+
if (job?.action && iterAction(job.action, callbacks, ['cron', name, 'action'])) {
|
|
149
144
|
return true;
|
|
150
145
|
}
|
|
151
146
|
}
|
package/jsonschema.js
CHANGED
|
@@ -57,7 +57,6 @@ export function generateDataFromSchema(schema) {
|
|
|
57
57
|
* @returns The combined schema.
|
|
58
58
|
*/
|
|
59
59
|
export function combineSchemas(...schemas) {
|
|
60
|
-
var _a;
|
|
61
60
|
const result = {};
|
|
62
61
|
for (const schema of schemas) {
|
|
63
62
|
if ('type' in schema) {
|
|
@@ -88,7 +87,7 @@ export function combineSchemas(...schemas) {
|
|
|
88
87
|
: schema.multipleOf;
|
|
89
88
|
}
|
|
90
89
|
if ('default' in schema) {
|
|
91
|
-
|
|
90
|
+
result.default ?? (result.default = schema.default);
|
|
92
91
|
}
|
|
93
92
|
if (schema.description) {
|
|
94
93
|
result.description || (result.description = schema.description);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appsemble/utils",
|
|
3
|
-
"version": "0.30.14-test.
|
|
3
|
+
"version": "0.30.14-test.4",
|
|
4
4
|
"description": "Utility functions used in Appsemble internally",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"app",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"test": "vitest"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@appsemble/types": "0.30.14-test.
|
|
40
|
+
"@appsemble/types": "0.30.14-test.4",
|
|
41
41
|
"axios": "^1.0.0",
|
|
42
42
|
"cron-parser": "^4.0.0",
|
|
43
43
|
"date-fns": "^2.0.0",
|
|
@@ -63,6 +63,6 @@
|
|
|
63
63
|
"vitest": "^1.0.0"
|
|
64
64
|
},
|
|
65
65
|
"engines": {
|
|
66
|
-
"node": ">=
|
|
66
|
+
"node": ">=20"
|
|
67
67
|
}
|
|
68
68
|
}
|
package/remap.js
CHANGED
|
@@ -82,7 +82,7 @@ const mapperImplementations = {
|
|
|
82
82
|
}
|
|
83
83
|
result = result[p];
|
|
84
84
|
}
|
|
85
|
-
return result
|
|
85
|
+
return result ?? null;
|
|
86
86
|
},
|
|
87
87
|
variable(name, input, context) {
|
|
88
88
|
if (context.getVariable) {
|
|
@@ -166,8 +166,8 @@ const mapperImplementations = {
|
|
|
166
166
|
return remap(condition ? mappers.then : mappers.else, input, context);
|
|
167
167
|
},
|
|
168
168
|
match(mappers, input, context) {
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
return (remap(mappers.find((mapper) => remap(mapper.case, input, context))?.value, input, context) ??
|
|
170
|
+
null);
|
|
171
171
|
},
|
|
172
172
|
'object.from': (mappers, input, context) => mapValues(mappers, (mapper) => remap(mapper, input, context)),
|
|
173
173
|
'object.assign': (mappers, input, context) => ({
|
|
@@ -184,7 +184,7 @@ const mapperImplementations = {
|
|
|
184
184
|
delete acc[k];
|
|
185
185
|
}
|
|
186
186
|
else {
|
|
187
|
-
acc = acc
|
|
187
|
+
acc = acc?.[k];
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
}
|
|
@@ -204,13 +204,10 @@ const mapperImplementations = {
|
|
|
204
204
|
}
|
|
205
205
|
return typeof input;
|
|
206
206
|
},
|
|
207
|
-
'array.map': (mapper, input, context) => {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
array: { index, length: input.length, item },
|
|
212
|
-
}))) !== null && _a !== void 0 ? _a : [];
|
|
213
|
-
},
|
|
207
|
+
'array.map': (mapper, input, context) => input?.map((item, index) => remap(mapper, item, {
|
|
208
|
+
...context,
|
|
209
|
+
array: { index, length: input.length, item },
|
|
210
|
+
})) ?? [],
|
|
214
211
|
'array.unique'(mapper, input, context) {
|
|
215
212
|
if (!Array.isArray(input)) {
|
|
216
213
|
return input;
|
|
@@ -230,13 +227,13 @@ const mapperImplementations = {
|
|
|
230
227
|
return true;
|
|
231
228
|
});
|
|
232
229
|
},
|
|
233
|
-
array: (prop, input, context) =>
|
|
230
|
+
array: (prop, input, context) => context.array?.[prop],
|
|
234
231
|
'array.filter'(mapper, input, context) {
|
|
235
232
|
if (!Array.isArray(input)) {
|
|
236
233
|
console.error(`${input} is not an array!`);
|
|
237
234
|
return null;
|
|
238
235
|
}
|
|
239
|
-
return input
|
|
236
|
+
return input?.filter((item, index) => {
|
|
240
237
|
const remapped = remap(mapper, item, {
|
|
241
238
|
...context,
|
|
242
239
|
array: { index, length: input.length, item },
|
|
@@ -245,12 +242,11 @@ const mapperImplementations = {
|
|
|
245
242
|
});
|
|
246
243
|
},
|
|
247
244
|
'array.find'(mapper, input, context) {
|
|
248
|
-
var _a;
|
|
249
245
|
if (!Array.isArray(input)) {
|
|
250
246
|
console.error(`${input} is not an array!`);
|
|
251
247
|
return null;
|
|
252
248
|
}
|
|
253
|
-
return (
|
|
249
|
+
return (input?.find((item) => {
|
|
254
250
|
const remapped = remap(mapper, item, context);
|
|
255
251
|
switch (typeof remapped) {
|
|
256
252
|
case 'boolean':
|
|
@@ -258,7 +254,7 @@ const mapperImplementations = {
|
|
|
258
254
|
default:
|
|
259
255
|
return equal(remapped, item) ? item : null;
|
|
260
256
|
}
|
|
261
|
-
})
|
|
257
|
+
}) ?? null);
|
|
262
258
|
},
|
|
263
259
|
'array.from': (mappers, input, context) => mappers.map((mapper) => remap(mapper, input, context)),
|
|
264
260
|
'array.append': (mappers, input, context) => Array.isArray(input)
|
|
@@ -379,7 +375,7 @@ const mapperImplementations = {
|
|
|
379
375
|
return result.join('');
|
|
380
376
|
},
|
|
381
377
|
root: (args, input, context) => context.root,
|
|
382
|
-
history: (index, input, context) =>
|
|
378
|
+
history: (index, input, context) => context.history?.[index],
|
|
383
379
|
'from.history': ({ index, props }, input, context) => mapValues(props, (mapper) => remap(mapper, context.history[index], context)),
|
|
384
380
|
'assign.history': ({ index, props }, input, context) => ({
|
|
385
381
|
...input,
|
|
@@ -395,7 +391,7 @@ const mapperImplementations = {
|
|
|
395
391
|
delete acc[k];
|
|
396
392
|
}
|
|
397
393
|
else {
|
|
398
|
-
acc = acc
|
|
394
|
+
acc = acc?.[k];
|
|
399
395
|
}
|
|
400
396
|
}
|
|
401
397
|
}
|
|
@@ -415,7 +411,7 @@ const mapperImplementations = {
|
|
|
415
411
|
return input;
|
|
416
412
|
},
|
|
417
413
|
log(level, input, context) {
|
|
418
|
-
console[level
|
|
414
|
+
console[level ?? 'info'](JSON.stringify({ input, context }, null, 2));
|
|
419
415
|
return input;
|
|
420
416
|
},
|
|
421
417
|
'string.format'({ messageId, template, values }, input, context) {
|
|
@@ -439,7 +435,7 @@ const mapperImplementations = {
|
|
|
439
435
|
const message = context.getMessage({ id: messageId });
|
|
440
436
|
return message.format() || `{${messageId}}`;
|
|
441
437
|
},
|
|
442
|
-
'app.member': (property, input, context) =>
|
|
438
|
+
'app.member': (property, input, context) => context.appMemberInfo?.[property],
|
|
443
439
|
container(property, input, context) {
|
|
444
440
|
// This value is replaced when the request is made
|
|
445
441
|
// By using the value of the release name
|
package/remap.test.js
CHANGED
|
@@ -5,8 +5,8 @@ import { remap } from './remap.js';
|
|
|
5
5
|
function runTests(tests) {
|
|
6
6
|
it.each(Object.entries(tests))('should %s', (name, { appMemberInfo, context, expected, history, input, mappers, messages, variables }) => {
|
|
7
7
|
const result = remap(mappers, input, {
|
|
8
|
-
getMessage: ({ defaultMessage, id }) =>
|
|
9
|
-
getVariable: (variableName) =>
|
|
8
|
+
getMessage: ({ defaultMessage, id }) => new IntlMessageFormat(messages?.messageIds?.[id] ?? defaultMessage),
|
|
9
|
+
getVariable: (variableName) => variables.find((variable) => variable.name === variableName)?.value,
|
|
10
10
|
url: 'https://example.com/en/example',
|
|
11
11
|
appUrl: 'https://example.com',
|
|
12
12
|
context,
|
|
@@ -287,7 +287,7 @@ describe('log', () => {
|
|
|
287
287
|
},
|
|
288
288
|
}, null, 2);
|
|
289
289
|
remap(mappers, input, {
|
|
290
|
-
getMessage: ({ defaultMessage, id }) =>
|
|
290
|
+
getMessage: ({ defaultMessage, id }) => new IntlMessageFormat(messages?.messageIds?.[id] ?? defaultMessage),
|
|
291
291
|
getVariable: (variableName) => variables.find((variable) => variable.name === variableName).value,
|
|
292
292
|
url: 'https://example.com/en/example',
|
|
293
293
|
appUrl: 'https://example.com',
|
package/validateAppMessages.js
CHANGED
|
@@ -8,7 +8,6 @@ export class AppMessageValidationError extends Error {
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
export function validateMessages(messages, app) {
|
|
11
|
-
var _a, _b;
|
|
12
11
|
const blockMessageKeys = {};
|
|
13
12
|
const extractedMessages = extractAppMessages(app, (block) => {
|
|
14
13
|
const type = normalizeBlockName(block.type);
|
|
@@ -31,7 +30,7 @@ export function validateMessages(messages, app) {
|
|
|
31
30
|
if (messages.app) {
|
|
32
31
|
Object.keys(messages.app).map((key) => {
|
|
33
32
|
const match = /^(pages\.[\dA-Za-z-]+(\..+)?)\.blocks\.\d+.+/.exec(key);
|
|
34
|
-
if ((!has(extractedMessages
|
|
33
|
+
if ((!has(extractedMessages?.app, key) || typeof messages.app[key] !== 'string') && !match) {
|
|
35
34
|
throw new AppMessageValidationError(`Invalid key ${key}`);
|
|
36
35
|
}
|
|
37
36
|
});
|
|
@@ -44,14 +43,14 @@ export function validateMessages(messages, app) {
|
|
|
44
43
|
}
|
|
45
44
|
}
|
|
46
45
|
}
|
|
47
|
-
const coreMessages =
|
|
46
|
+
const coreMessages = messages.core ?? {};
|
|
48
47
|
for (const [key, value] of Object.entries(coreMessages)) {
|
|
49
48
|
if (typeof value !== 'string') {
|
|
50
49
|
throw new AppMessageValidationError(`Invalid translation key: core.${key}`);
|
|
51
50
|
}
|
|
52
51
|
}
|
|
53
52
|
for (const [blockName] of Object.entries(blockMessageKeys)) {
|
|
54
|
-
if (
|
|
53
|
+
if (messages.blocks?.[blockName]) {
|
|
55
54
|
blockMessages[blockName] = {};
|
|
56
55
|
for (const [version, oldValues] of Object.entries(messages.blocks[blockName])) {
|
|
57
56
|
if (!has(blockMessageKeys[blockName], version)) {
|
package/validation.js
CHANGED
|
@@ -73,13 +73,12 @@ function validateUniquePageNames(definition, report) {
|
|
|
73
73
|
checkPages(definition.pages);
|
|
74
74
|
}
|
|
75
75
|
function validateMembersSchema(definition, report) {
|
|
76
|
-
var _a;
|
|
77
76
|
if (!definition.members) {
|
|
78
77
|
return;
|
|
79
78
|
}
|
|
80
79
|
for (const [propertyName, propertyDefinition] of Object.entries(definition.members.properties)) {
|
|
81
80
|
// Handled by schema validation
|
|
82
|
-
if (!
|
|
81
|
+
if (!propertyDefinition?.schema) {
|
|
83
82
|
continue;
|
|
84
83
|
}
|
|
85
84
|
const { schema } = propertyDefinition;
|
|
@@ -90,7 +89,7 @@ function validateMembersSchema(definition, report) {
|
|
|
90
89
|
}
|
|
91
90
|
if ('reference' in propertyDefinition) {
|
|
92
91
|
const { resource: resourceName } = propertyDefinition.reference;
|
|
93
|
-
const resourceDefinition =
|
|
92
|
+
const resourceDefinition = definition.resources?.[resourceName];
|
|
94
93
|
if (!resourceDefinition) {
|
|
95
94
|
report(resourceName, 'refers to a resource that doesn’t exist', [
|
|
96
95
|
'members',
|
|
@@ -109,7 +108,7 @@ function validateResourceSchemas(definition, report) {
|
|
|
109
108
|
}
|
|
110
109
|
for (const [resourceName, resource] of Object.entries(definition.resources)) {
|
|
111
110
|
// Handled by schema validation
|
|
112
|
-
if (!
|
|
111
|
+
if (!resource?.schema) {
|
|
113
112
|
continue;
|
|
114
113
|
}
|
|
115
114
|
const { schema } = resource;
|
|
@@ -208,7 +207,6 @@ function validateController(definition, controllerImplementations, report) {
|
|
|
208
207
|
}
|
|
209
208
|
iterApp(definition, {
|
|
210
209
|
onController(controller, path) {
|
|
211
|
-
var _a, _b, _c, _d, _e, _f;
|
|
212
210
|
const actionParameters = new Set();
|
|
213
211
|
if (controller.actions) {
|
|
214
212
|
if (controllerImplementations.actions) {
|
|
@@ -251,16 +249,16 @@ function validateController(definition, controllerImplementations, report) {
|
|
|
251
249
|
}
|
|
252
250
|
if (controller.events.emit) {
|
|
253
251
|
for (const [key, value] of Object.entries(controller.events.emit)) {
|
|
254
|
-
if (!
|
|
255
|
-
!has(
|
|
252
|
+
if (!controllerImplementations.events?.emit?.$any &&
|
|
253
|
+
!has(controllerImplementations.events?.emit, key)) {
|
|
256
254
|
report(value, 'is an unknown event emitter', [...path, 'events', 'emit', key]);
|
|
257
255
|
}
|
|
258
256
|
}
|
|
259
257
|
}
|
|
260
258
|
if (controller.events.listen) {
|
|
261
259
|
for (const [key, value] of Object.entries(controller.events.listen)) {
|
|
262
|
-
if (!
|
|
263
|
-
!has(
|
|
260
|
+
if (!controllerImplementations.events?.listen?.$any &&
|
|
261
|
+
!has(controllerImplementations.events?.listen, key)) {
|
|
264
262
|
report(value, 'is an unknown event listener', [...path, 'events', 'listen', key]);
|
|
265
263
|
}
|
|
266
264
|
}
|
|
@@ -271,7 +269,6 @@ function validateController(definition, controllerImplementations, report) {
|
|
|
271
269
|
function validateBlocks(definition, blockVersions, report) {
|
|
272
270
|
iterApp(definition, {
|
|
273
271
|
onBlock(block, path) {
|
|
274
|
-
var _a, _b, _c, _d, _e, _f;
|
|
275
272
|
const type = normalizeBlockName(block.type);
|
|
276
273
|
const versions = blockVersions.get(type);
|
|
277
274
|
if (!versions) {
|
|
@@ -292,8 +289,8 @@ function validateBlocks(definition, blockVersions, report) {
|
|
|
292
289
|
actionParameters.add(property);
|
|
293
290
|
return has(block.actions, property);
|
|
294
291
|
};
|
|
295
|
-
validator.customFormats['event-listener'] = (property) =>
|
|
296
|
-
validator.customFormats['event-emitter'] = (property) =>
|
|
292
|
+
validator.customFormats['event-listener'] = (property) => has(block.events?.listen, property);
|
|
293
|
+
validator.customFormats['event-emitter'] = (property) => has(block.events?.emit, property);
|
|
297
294
|
const result = validator.validate(block.parameters || {}, version.parameters, {
|
|
298
295
|
nestedErrors: true,
|
|
299
296
|
});
|
|
@@ -334,14 +331,14 @@ function validateBlocks(definition, blockVersions, report) {
|
|
|
334
331
|
}
|
|
335
332
|
if (block.events.emit) {
|
|
336
333
|
for (const [key, value] of Object.entries(block.events.emit)) {
|
|
337
|
-
if (!
|
|
334
|
+
if (!version.events?.emit?.$any && !has(version.events?.emit, key)) {
|
|
338
335
|
report(value, 'is an unknown event emitter', [...path, 'events', 'emit', key]);
|
|
339
336
|
}
|
|
340
337
|
}
|
|
341
338
|
}
|
|
342
339
|
if (block.events.listen) {
|
|
343
340
|
for (const [key, value] of Object.entries(block.events.listen)) {
|
|
344
|
-
if (!
|
|
341
|
+
if (!version.events?.listen?.$any && !has(version.events?.listen, key)) {
|
|
345
342
|
report(value, 'is an unknown event listener', [...path, 'events', 'listen', key]);
|
|
346
343
|
}
|
|
347
344
|
}
|
|
@@ -350,7 +347,6 @@ function validateBlocks(definition, blockVersions, report) {
|
|
|
350
347
|
});
|
|
351
348
|
}
|
|
352
349
|
function validatePermissions(appDefinition, permissions, inheritedPermissions, possiblePermissions, report, path) {
|
|
353
|
-
var _a, _b, _c, _d;
|
|
354
350
|
const checked = [];
|
|
355
351
|
for (const [index, permission] of permissions.entries()) {
|
|
356
352
|
if (checked.includes(permission)) {
|
|
@@ -361,7 +357,7 @@ function validatePermissions(appDefinition, permissions, inheritedPermissions, p
|
|
|
361
357
|
if (resourcePermissionPattern.test(permission) ||
|
|
362
358
|
ownResourcePermissionPattern.test(permission)) {
|
|
363
359
|
const [, resourceName] = permission.split(':');
|
|
364
|
-
if (resourceName && resourceName !== 'all' && !
|
|
360
|
+
if (resourceName && resourceName !== 'all' && !appDefinition.resources?.[resourceName]) {
|
|
365
361
|
report(appDefinition, `resource ${resourceName} does not exist in the app's resources definition`, [...path, 'permissions', index]);
|
|
366
362
|
return;
|
|
367
363
|
}
|
|
@@ -370,14 +366,14 @@ function validatePermissions(appDefinition, permissions, inheritedPermissions, p
|
|
|
370
366
|
const [, resourceName, , resourceView] = permission.split(':');
|
|
371
367
|
if (resourceName === 'all') {
|
|
372
368
|
for (const [rName, resourceDefinition] of Object.entries(appDefinition.resources)) {
|
|
373
|
-
if (!
|
|
369
|
+
if (!resourceDefinition.views?.[resourceView]) {
|
|
374
370
|
report(appDefinition, `resource ${rName} is missing a definition for the ${resourceView} view`, [...path, 'permissions', index]);
|
|
375
371
|
return;
|
|
376
372
|
}
|
|
377
373
|
}
|
|
378
374
|
}
|
|
379
375
|
else {
|
|
380
|
-
if (!
|
|
376
|
+
if (!appDefinition.resources[resourceName]?.views?.[resourceView]) {
|
|
381
377
|
report(appDefinition, `resource ${resourceName} is missing a definition for the ${resourceView} view`, [...path, 'permissions', index]);
|
|
382
378
|
return;
|
|
383
379
|
}
|
|
@@ -616,13 +612,12 @@ function checkCyclicRoleInheritance(roles, name, report) {
|
|
|
616
612
|
let lastChecked;
|
|
617
613
|
const stack = [];
|
|
618
614
|
const checkRoleRecursively = (role) => {
|
|
619
|
-
var _a, _b;
|
|
620
615
|
lastChecked = role;
|
|
621
616
|
if (stack.includes(role)) {
|
|
622
617
|
return true;
|
|
623
618
|
}
|
|
624
619
|
stack.push(role);
|
|
625
|
-
return
|
|
620
|
+
return roles[role]?.inherits?.some(checkRoleRecursively);
|
|
626
621
|
};
|
|
627
622
|
const duplicate = checkRoleRecursively(name);
|
|
628
623
|
if (duplicate && lastChecked === name) {
|
|
@@ -646,7 +641,7 @@ function validateSecurity(definition, report) {
|
|
|
646
641
|
return true;
|
|
647
642
|
};
|
|
648
643
|
const checkRoles = (object, path) => {
|
|
649
|
-
if (!
|
|
644
|
+
if (!object?.roles) {
|
|
650
645
|
return;
|
|
651
646
|
}
|
|
652
647
|
for (const [index, role] of object.roles.entries()) {
|
|
@@ -692,7 +687,7 @@ function validateSecurity(definition, report) {
|
|
|
692
687
|
report(definition, `not allowed to overwrite role ${name}`, ['security', 'roles', name]);
|
|
693
688
|
}
|
|
694
689
|
const inheritedPermissions = [];
|
|
695
|
-
if (role
|
|
690
|
+
if (role?.inherits) {
|
|
696
691
|
let found = false;
|
|
697
692
|
for (const [index, inherited] of (role.inherits || []).entries()) {
|
|
698
693
|
found || (found = checkRoleExists(inherited, ['security', 'roles', name, 'inherits', index]));
|
|
@@ -731,7 +726,6 @@ function validateSecurity(definition, report) {
|
|
|
731
726
|
* @param report A function used to report a value.
|
|
732
727
|
*/
|
|
733
728
|
function validateHooks(definition, report) {
|
|
734
|
-
var _a, _b, _c;
|
|
735
729
|
if (!definition.resources) {
|
|
736
730
|
return;
|
|
737
731
|
}
|
|
@@ -741,10 +735,10 @@ function validateHooks(definition, report) {
|
|
|
741
735
|
if (!has(resource, actionType)) {
|
|
742
736
|
continue;
|
|
743
737
|
}
|
|
744
|
-
const tos =
|
|
738
|
+
const tos = resource[actionType].hooks?.notification?.to;
|
|
745
739
|
if (tos) {
|
|
746
740
|
for (const [index, to] of tos.entries()) {
|
|
747
|
-
if (to !== '$author' && !has(
|
|
741
|
+
if (to !== '$author' && !has(definition.security?.roles, to)) {
|
|
748
742
|
report(to, 'is an unknown role', [
|
|
749
743
|
'resources',
|
|
750
744
|
resourceKey,
|
|
@@ -810,7 +804,7 @@ function validateCronJobs({ cron }, report) {
|
|
|
810
804
|
return;
|
|
811
805
|
}
|
|
812
806
|
for (const [id, job] of Object.entries(cron)) {
|
|
813
|
-
if (typeof
|
|
807
|
+
if (typeof job?.schedule !== 'string') {
|
|
814
808
|
continue;
|
|
815
809
|
}
|
|
816
810
|
try {
|
|
@@ -825,7 +819,6 @@ function validateActions(definition, report) {
|
|
|
825
819
|
const urlRegex = new RegExp(`^${partialNormalized.source}:`);
|
|
826
820
|
iterApp(definition, {
|
|
827
821
|
onAction(action, path) {
|
|
828
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
829
822
|
if (path[0] === 'cron' && !serverActions.has(action.type)) {
|
|
830
823
|
report(action.type, 'action type is not supported for cron jobs', [...path, 'type']);
|
|
831
824
|
return;
|
|
@@ -835,10 +828,10 @@ function validateActions(definition, report) {
|
|
|
835
828
|
return;
|
|
836
829
|
}
|
|
837
830
|
if (['app.member.register', 'app.member.properties.patch', 'app.member.current.patch'].includes(action.type) &&
|
|
838
|
-
Object.values(
|
|
839
|
-
|
|
840
|
-
for (const propertyName of Object.keys(Object.values(
|
|
841
|
-
if (!
|
|
831
|
+
Object.values(action.properties ?? {})[0] &&
|
|
832
|
+
definition.members?.properties) {
|
|
833
|
+
for (const propertyName of Object.keys(Object.values(action.properties ?? {})[0])) {
|
|
834
|
+
if (!definition.members?.properties[propertyName]) {
|
|
842
835
|
report(action.type, 'contains a property that doesn’t exist in app member properties', [
|
|
843
836
|
...path,
|
|
844
837
|
'properties',
|
|
@@ -849,7 +842,7 @@ function validateActions(definition, report) {
|
|
|
849
842
|
if (action.type.startsWith('resource.')) {
|
|
850
843
|
// All of the actions starting with `resource.` contain a property called `resource`.
|
|
851
844
|
const { resource: resourceName, view } = action;
|
|
852
|
-
const resource =
|
|
845
|
+
const resource = definition.resources?.[resourceName];
|
|
853
846
|
const [, resourceAction] = action.type.split('.');
|
|
854
847
|
if (!resource) {
|
|
855
848
|
report(action.type, 'refers to a resource that doesn’t exist', [...path, 'resource']);
|
|
@@ -860,7 +853,7 @@ function validateActions(definition, report) {
|
|
|
860
853
|
report(action.type, 'missing security definition', [...path, 'resource']);
|
|
861
854
|
return;
|
|
862
855
|
}
|
|
863
|
-
const allPermissions =
|
|
856
|
+
const allPermissions = definition.security.guest?.permissions || [];
|
|
864
857
|
if (definition.security.roles) {
|
|
865
858
|
const allRolePermissions = getAppRolePermissions(definition.security, Object.keys(definition.security.roles));
|
|
866
859
|
allPermissions.push(...allRolePermissions);
|
|
@@ -899,19 +892,19 @@ function validateActions(definition, report) {
|
|
|
899
892
|
}
|
|
900
893
|
}
|
|
901
894
|
if (action.type.startsWith('flow.')) {
|
|
902
|
-
const page =
|
|
895
|
+
const page = definition.pages?.[Number(path[1])];
|
|
903
896
|
if (page.type !== 'flow' && page.type !== 'loop') {
|
|
904
897
|
report(action.type, 'flow actions can only be used on pages with the type ‘flow’ or ‘loop’', [...path, 'type']);
|
|
905
898
|
return;
|
|
906
899
|
}
|
|
907
|
-
if (action.type === 'flow.cancel' && !
|
|
900
|
+
if (action.type === 'flow.cancel' && !page.actions?.onFlowCancel) {
|
|
908
901
|
report(action.type, 'was defined but ‘onFlowCancel’ page action wasn’t defined', [
|
|
909
902
|
...path,
|
|
910
903
|
'type',
|
|
911
904
|
]);
|
|
912
905
|
return;
|
|
913
906
|
}
|
|
914
|
-
if (action.type === 'flow.finish' && !
|
|
907
|
+
if (action.type === 'flow.finish' && !page.actions?.onFlowFinish) {
|
|
915
908
|
report(action.type, 'was defined but ‘onFlowFinish’ page action wasn’t defined', [
|
|
916
909
|
...path,
|
|
917
910
|
'type',
|
|
@@ -925,7 +918,7 @@ function validateActions(definition, report) {
|
|
|
925
918
|
if (page.type === 'flow' &&
|
|
926
919
|
action.type === 'flow.next' &&
|
|
927
920
|
Number(path[3]) === page.steps.length - 1 &&
|
|
928
|
-
!
|
|
921
|
+
!page.actions?.onFlowFinish) {
|
|
929
922
|
report(action.type, 'was defined on the last step but ‘onFlowFinish’ page action wasn’t defined', [...path, 'type']);
|
|
930
923
|
return;
|
|
931
924
|
}
|