@appsemble/utils 0.30.2 → 0.30.8

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.
Files changed (37) hide show
  1. package/README.md +3 -3
  2. package/api/components/parameters/roles.js +1 -11
  3. package/api/components/schemas/ActionDefinition.js +1 -0
  4. package/api/components/schemas/ContainerPageDefinition.js +2 -1
  5. package/api/components/schemas/FlowPageDefinition.js +1 -1
  6. package/api/components/schemas/LoopPageDefinition.js +1 -7
  7. package/api/components/schemas/ResourceCountActionDefinition.js +1 -1
  8. package/api/components/schemas/ResourceDeleteActionDefinition.js +1 -1
  9. package/api/components/schemas/ResourceGetActionDefinition.js +1 -1
  10. package/api/components/schemas/ResourceHistoryGetActionDefinition.d.ts +1 -0
  11. package/api/components/schemas/ResourceHistoryGetActionDefinition.js +18 -0
  12. package/api/components/schemas/ResourcePatchActionDefinition.js +1 -1
  13. package/api/components/schemas/ResourceQueryActionDefinition.js +1 -1
  14. package/api/components/schemas/ResourceUpdateActionDefinition.js +1 -1
  15. package/api/components/schemas/TabsPageDefinition.js +1 -1
  16. package/api/components/schemas/index.d.ts +1 -0
  17. package/api/components/schemas/index.js +1 -0
  18. package/api/paths/apps/appId/assets.js +1 -1
  19. package/api/paths/apps/appId/invites/resend.d.ts +2 -0
  20. package/api/paths/apps/appId/invites/resend.js +32 -0
  21. package/api/paths/apps/appId/invites.js +29 -0
  22. package/api/paths/apps/appId/resources/versions.js +2 -1
  23. package/api/paths/apps.js +2 -2
  24. package/api/paths/groups/groupId/invites/resend.d.ts +2 -0
  25. package/api/paths/groups/groupId/invites/resend.js +32 -0
  26. package/api/paths/groups/groupId/invites.js +29 -0
  27. package/api/paths/index.d.ts +2 -0
  28. package/api/paths/index.js +4 -0
  29. package/api/paths/resourceHistory.d.ts +2 -0
  30. package/api/paths/resourceHistory.js +31 -0
  31. package/api/paths/users/current/apps.js +2 -2
  32. package/authorization.js +4 -6
  33. package/package.json +2 -2
  34. package/remap.js +4 -1
  35. package/remap.test.js +10 -0
  36. package/validation.js +3 -2
  37. package/validation.test.js +18 -12
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
- # ![](https://gitlab.com/appsemble/appsemble/-/raw/0.30.2/config/assets/logo.svg) Appsemble Utilities
1
+ # ![](https://gitlab.com/appsemble/appsemble/-/raw/0.30.8/config/assets/logo.svg) Appsemble Utilities
2
2
 
3
3
  > Internal utility functions used across multiple Appsemble projects.
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/@appsemble/utils)](https://www.npmjs.com/package/@appsemble/utils)
6
- [![GitLab CI](https://gitlab.com/appsemble/appsemble/badges/0.30.2/pipeline.svg)](https://gitlab.com/appsemble/appsemble/-/releases/0.30.2)
6
+ [![GitLab CI](https://gitlab.com/appsemble/appsemble/badges/0.30.8/pipeline.svg)](https://gitlab.com/appsemble/appsemble/-/releases/0.30.8)
7
7
  [![Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](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.2/LICENSE.md) ©
29
+ [LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.30.8/LICENSE.md) ©
30
30
  [Appsemble](https://appsemble.com)
@@ -3,17 +3,7 @@ export const roles = {
3
3
  in: 'query',
4
4
  description: 'The roles of app members on which to perform an operation',
5
5
  schema: {
6
- oneOf: [
7
- {
8
- type: 'array',
9
- items: {
10
- $ref: '#/components/schemas/AppMember/properties/role',
11
- },
12
- },
13
- {
14
- type: 'string',
15
- },
16
- ],
6
+ type: 'string',
17
7
  },
18
8
  };
19
9
  //# sourceMappingURL=roles.js.map
@@ -53,6 +53,7 @@ export const ActionDefinition = {
53
53
  { $ref: '#/components/schemas/ResourceCreateActionDefinition' },
54
54
  { $ref: '#/components/schemas/ResourceDeleteActionDefinition' },
55
55
  { $ref: '#/components/schemas/ResourceGetActionDefinition' },
56
+ { $ref: '#/components/schemas/ResourceHistoryGetActionDefinition' },
56
57
  { $ref: '#/components/schemas/ResourceQueryActionDefinition' },
57
58
  { $ref: '#/components/schemas/ResourceSubscriptionStatusActionDefinition' },
58
59
  { $ref: '#/components/schemas/ResourceSubscriptionSubscribeActionDefinition' },
@@ -3,7 +3,8 @@ import { extendJSONSchema } from './utils.js';
3
3
  export const ContainerPageDefinition = extendJSONSchema(BasePageDefinition, {
4
4
  type: 'object',
5
5
  additionalProperties: false,
6
- description: `Use this page type to group pages in the menu, this doesn't actually group pages for now. Following is an example of how this can be used
6
+ description: `Groups pages together under a collapsible parent page. Following is an example of
7
+ how this can be used:
7
8
  \`\`\`yaml
8
9
  pages:
9
10
  - name: Page 1
@@ -2,7 +2,7 @@ import { BasePageDefinition } from './BasePageDefinition.js';
2
2
  import { extendJSONSchema } from './utils.js';
3
3
  export const FlowPageDefinition = extendJSONSchema(BasePageDefinition, {
4
4
  type: 'object',
5
- description: 'This describes what a page will look like in the app.',
5
+ description: 'Shows a set of sub pages in a certain order which the user can navigate through.',
6
6
  required: ['type', 'steps'],
7
7
  additionalProperties: false,
8
8
  properties: {
@@ -2,13 +2,7 @@ import { BasePageDefinition } from './BasePageDefinition.js';
2
2
  import { extendJSONSchema } from './utils.js';
3
3
  export const LoopPageDefinition = extendJSONSchema(BasePageDefinition, {
4
4
  type: 'object',
5
- description: `This describes what a loop page will look like in the app.
6
-
7
- !!
8
- This feature is still under development and is very unstable
9
- !!
10
-
11
- `,
5
+ description: 'Generates sub pages dynamically based on data and display in a flow page.',
12
6
  required: ['type', 'foreach', 'actions'],
13
7
  additionalProperties: false,
14
8
  properties: {
@@ -18,5 +18,5 @@ export const ResourceCountActionDefinition = extendJSONSchema(RequestActionDefin
18
18
  description: 'If only the resources created by the authenticated app member should be included',
19
19
  },
20
20
  },
21
- }, ['url']);
21
+ }, ['url', 'method']);
22
22
  //# sourceMappingURL=ResourceCountActionDefinition.js.map
@@ -14,5 +14,5 @@ export const ResourceDeleteActionDefinition = extendJSONSchema(RequestActionDefi
14
14
  description: 'The type of the resource to delete.',
15
15
  },
16
16
  },
17
- }, ['url']);
17
+ }, ['url', 'method']);
18
18
  //# sourceMappingURL=ResourceDeleteActionDefinition.js.map
@@ -18,5 +18,5 @@ export const ResourceGetActionDefinition = extendJSONSchema(RequestActionDefinit
18
18
  description: 'The view to use for the resource.',
19
19
  },
20
20
  },
21
- }, ['url']);
21
+ }, ['url', 'method']);
22
22
  //# sourceMappingURL=ResourceGetActionDefinition.js.map
@@ -0,0 +1 @@
1
+ export declare const ResourceHistoryGetActionDefinition: import("openapi-types").OpenAPIV3.SchemaObject;
@@ -0,0 +1,18 @@
1
+ import { RequestActionDefinition } from './RequestActionDefinition.js';
2
+ import { extendJSONSchema } from './utils.js';
3
+ export const ResourceHistoryGetActionDefinition = extendJSONSchema(RequestActionDefinition, {
4
+ type: 'object',
5
+ additionalProperties: false,
6
+ required: ['type', 'resource'],
7
+ properties: {
8
+ type: {
9
+ enum: ['resource.history.get'],
10
+ description: 'Get the complete history of a resource.',
11
+ },
12
+ resource: {
13
+ type: 'string',
14
+ description: 'The type of the resource to get.',
15
+ },
16
+ },
17
+ }, ['url']);
18
+ //# sourceMappingURL=ResourceHistoryGetActionDefinition.js.map
@@ -14,5 +14,5 @@ export const ResourcePatchActionDefinition = extendJSONSchema(RequestActionDefin
14
14
  description: 'The type of the resource to patch.',
15
15
  },
16
16
  },
17
- }, ['url']);
17
+ }, ['url', 'method']);
18
18
  //# sourceMappingURL=ResourcePatchActionDefinition.js.map
@@ -22,5 +22,5 @@ export const ResourceQueryActionDefinition = extendJSONSchema(RequestActionDefin
22
22
  description: 'If only the resources created by the authenticated app member should be included',
23
23
  },
24
24
  },
25
- }, ['url']);
25
+ }, ['url', 'method']);
26
26
  //# sourceMappingURL=ResourceQueryActionDefinition.js.map
@@ -14,5 +14,5 @@ export const ResourceUpdateActionDefinition = extendJSONSchema(RequestActionDefi
14
14
  description: 'The type of the resource to update.',
15
15
  },
16
16
  },
17
- }, ['url']);
17
+ }, ['url', 'method']);
18
18
  //# sourceMappingURL=ResourceUpdateActionDefinition.js.map
@@ -2,7 +2,7 @@ import { BasePageDefinition } from './BasePageDefinition.js';
2
2
  import { extendJSONSchema } from './utils.js';
3
3
  export const TabsPageDefinition = extendJSONSchema(BasePageDefinition, {
4
4
  type: 'object',
5
- description: 'This describes what a page will look like in the app.',
5
+ description: 'Shows a set of subpages as tabs at the top of the page.',
6
6
  oneOf: [
7
7
  {
8
8
  required: ['type', 'tabs'],
@@ -83,6 +83,7 @@ export * from './ResourceCreateActionDefinition.js';
83
83
  export * from './ResourceDefinition.js';
84
84
  export * from './ResourceDeleteActionDefinition.js';
85
85
  export * from './ResourceGetActionDefinition.js';
86
+ export * from './ResourceHistoryGetActionDefinition.js';
86
87
  export * from './ResourceHistoryDefinition.js';
87
88
  export * from './ResourceHooksDefinition.js';
88
89
  export * from './ResourceQueryActionDefinition.js';
@@ -83,6 +83,7 @@ export * from './ResourceCreateActionDefinition.js';
83
83
  export * from './ResourceDefinition.js';
84
84
  export * from './ResourceDeleteActionDefinition.js';
85
85
  export * from './ResourceGetActionDefinition.js';
86
+ export * from './ResourceHistoryGetActionDefinition.js';
86
87
  export * from './ResourceHistoryDefinition.js';
87
88
  export * from './ResourceHooksDefinition.js';
88
89
  export * from './ResourceQueryActionDefinition.js';
@@ -90,7 +90,7 @@ export const pathItems = {
90
90
  description: 'The app assets have been deleted successfully.',
91
91
  },
92
92
  },
93
- security: [{ studio: [] }, { app: ['resources:manage'] }, { cli: ['resources:write'] }, {}],
93
+ security: [{ studio: [] }, { app: ['resources:manage'] }, { cli: ['assets:write'] }, {}],
94
94
  },
95
95
  };
96
96
  //# sourceMappingURL=assets.js.map
@@ -0,0 +1,2 @@
1
+ import { type OpenAPIV3 } from 'openapi-types';
2
+ export declare const pathItems: OpenAPIV3.PathItemObject;
@@ -0,0 +1,32 @@
1
+ export const pathItems = {
2
+ parameters: [{ $ref: '#/components/parameters/appId' }],
3
+ post: {
4
+ tags: ['app'],
5
+ description: 'Request to resend an invitation.',
6
+ operationId: 'resendAppInvite',
7
+ requestBody: {
8
+ description: 'The email of the person to resend the invitation to.',
9
+ required: true,
10
+ content: {
11
+ 'application/json': {
12
+ schema: {
13
+ type: 'object',
14
+ required: ['email'],
15
+ properties: {
16
+ email: {
17
+ type: 'string',
18
+ },
19
+ },
20
+ },
21
+ },
22
+ },
23
+ },
24
+ responses: {
25
+ 204: {
26
+ description: 'The invite has been sent.',
27
+ },
28
+ },
29
+ security: [{ studio: [] }],
30
+ },
31
+ };
32
+ //# sourceMappingURL=resend.js.map
@@ -75,5 +75,34 @@ export const pathItems = {
75
75
  },
76
76
  security: [{ studio: [] }, { app: ['app:write'] }],
77
77
  },
78
+ delete: {
79
+ tags: ['app', 'invite'],
80
+ description: 'Revoke an app member invitation.',
81
+ operationId: 'deleteAppInvite',
82
+ requestBody: {
83
+ description: 'The email address to revoke the invite of.',
84
+ required: true,
85
+ content: {
86
+ 'application/json': {
87
+ schema: {
88
+ type: 'object',
89
+ required: ['email'],
90
+ properties: {
91
+ email: {
92
+ type: 'string',
93
+ format: 'email',
94
+ },
95
+ },
96
+ },
97
+ },
98
+ },
99
+ },
100
+ responses: {
101
+ 204: {
102
+ description: 'The invitation has been successfully revoked.',
103
+ },
104
+ },
105
+ security: [{ studio: [] }],
106
+ },
78
107
  };
79
108
  //# sourceMappingURL=invites.js.map
@@ -3,6 +3,7 @@ export const pathItems = {
3
3
  { $ref: '#/components/parameters/appId' },
4
4
  { $ref: '#/components/parameters/resourceType' },
5
5
  { $ref: '#/components/parameters/resourceId' },
6
+ { $ref: '#/components/parameters/selectedGroupId' },
6
7
  ],
7
8
  get: {
8
9
  tags: ['main', 'app', 'resource', 'version'],
@@ -23,7 +24,7 @@ export const pathItems = {
23
24
  },
24
25
  },
25
26
  },
26
- security: [{ studio: [] }],
27
+ security: [{ studio: [] }, { app: ['resources:manage'] }],
27
28
  },
28
29
  };
29
30
  //# sourceMappingURL=versions.js.map
package/api/paths/apps.js CHANGED
@@ -121,11 +121,11 @@ export const pathItems = {
121
121
  in: 'query',
122
122
  },
123
123
  ],
124
- description: 'Get all existing apps.',
124
+ description: 'Get all publically available apps.',
125
125
  operationId: 'queryApps',
126
126
  responses: {
127
127
  200: {
128
- description: 'The list of all apps.',
128
+ description: 'The list of all public apps.',
129
129
  content: {
130
130
  'application/json': {
131
131
  schema: {
@@ -0,0 +1,2 @@
1
+ import { type OpenAPIV3 } from 'openapi-types';
2
+ export declare const pathItems: OpenAPIV3.PathItemObject;
@@ -0,0 +1,32 @@
1
+ export const pathItems = {
2
+ parameters: [{ $ref: '#/components/parameters/groupId' }],
3
+ post: {
4
+ tags: ['group'],
5
+ description: 'Request to resend an invitation.',
6
+ operationId: 'resendGroupInvite',
7
+ requestBody: {
8
+ description: 'The email of the person to resend the invitation to.',
9
+ required: true,
10
+ content: {
11
+ 'application/json': {
12
+ schema: {
13
+ type: 'object',
14
+ required: ['email'],
15
+ properties: {
16
+ email: {
17
+ type: 'string',
18
+ },
19
+ },
20
+ },
21
+ },
22
+ },
23
+ },
24
+ responses: {
25
+ 204: {
26
+ description: 'The invite has been sent.',
27
+ },
28
+ },
29
+ security: [{ studio: [] }],
30
+ },
31
+ };
32
+ //# sourceMappingURL=resend.js.map
@@ -83,5 +83,34 @@ export const pathItems = {
83
83
  },
84
84
  security: [{ studio: [] }, { app: ['groups:write'] }, { cli: ['groups:write'] }],
85
85
  },
86
+ delete: {
87
+ tags: ['group', 'invite'],
88
+ description: 'Revoke a group member invitation.',
89
+ operationId: 'deleteGroupInvite',
90
+ requestBody: {
91
+ description: 'The email address to revoke the invite of.',
92
+ required: true,
93
+ content: {
94
+ 'application/json': {
95
+ schema: {
96
+ type: 'object',
97
+ required: ['email'],
98
+ properties: {
99
+ email: {
100
+ type: 'string',
101
+ format: 'email',
102
+ },
103
+ },
104
+ },
105
+ },
106
+ },
107
+ },
108
+ responses: {
109
+ 204: {
110
+ description: 'The invitation has been successfully revoked.',
111
+ },
112
+ },
113
+ security: [{ studio: [] }],
114
+ },
86
115
  };
87
116
  //# sourceMappingURL=invites.js.map
@@ -23,6 +23,7 @@ export declare const paths: {
23
23
  '/api/apps/{appId}/export': import("openapi-types").OpenAPIV3.PathItemObject<{}>;
24
24
  '/api/apps/{appId}/groups': import("openapi-types").OpenAPIV3.PathItemObject<{}>;
25
25
  '/api/apps/{appId}/icon': import("openapi-types").OpenAPIV3.PathItemObject<{}>;
26
+ '/api/apps/{appId}/invites/resend': import("openapi-types").OpenAPIV3.PathItemObject<{}>;
26
27
  '/api/apps/{appId}/invites': import("openapi-types").OpenAPIV3.PathItemObject<{}>;
27
28
  '/api/apps/{appId}/lock': import("openapi-types").OpenAPIV3.PathItemObject<{}>;
28
29
  '/api/apps/{appId}/maskable-icon': import("openapi-types").OpenAPIV3.PathItemObject<{}>;
@@ -121,6 +122,7 @@ export declare const paths: {
121
122
  '/api/group-invites/{token}': import("openapi-types").OpenAPIV3.PathItemObject<{}>;
122
123
  '/api/group-members/{groupMemberId}/role': import("openapi-types").OpenAPIV3.PathItemObject<{}>;
123
124
  '/api/group-members/{groupMemberId}': import("openapi-types").OpenAPIV3.PathItemObject<{}>;
125
+ '/api/groups/{groupId}/invites/resend': import("openapi-types").OpenAPIV3.PathItemObject<{}>;
124
126
  '/api/groups/{groupId}/invites': import("openapi-types").OpenAPIV3.PathItemObject<{}>;
125
127
  '/api/groups/{groupId}/members': import("openapi-types").OpenAPIV3.PathItemObject<{}>;
126
128
  '/api/groups/{groupId}': import("openapi-types").OpenAPIV3.PathItemObject<{}>;
@@ -28,6 +28,7 @@ import { pathItems as appsAppIdEmail } from './apps/appId/email.js';
28
28
  import { pathItems as appsAppIdExport } from './apps/appId/export.js';
29
29
  import { pathItems as appsAppIdGroups } from './apps/appId/groups.js';
30
30
  import { pathItems as appsAppIdIcon } from './apps/appId/icon.js';
31
+ import { pathItems as appsAppIdInvitesResend } from './apps/appId/invites/resend.js';
31
32
  import { pathItems as appsAppIdInvites } from './apps/appId/invites.js';
32
33
  import { pathItems as appsAppIdLock } from './apps/appId/lock.js';
33
34
  import { pathItems as appsAppIdMaskableIcon } from './apps/appId/maskableIcon.js';
@@ -102,6 +103,7 @@ import { pathItems as groupInvitesTokenRespond } from './group-invites/token/res
102
103
  import { pathItems as groupInvitesToken } from './group-invites/token.js';
103
104
  import { pathItems as groupsMembersGroupMemberIdRole } from './group-members/groupMemberId/role.js';
104
105
  import { pathItems as groupsMembersGroupMemberId } from './group-members/groupMemberId.js';
106
+ import { pathItems as groupsGroupIdInvitesResend } from './groups/groupId/invites/resend.js';
105
107
  import { pathItems as groupsGroupIdInvites } from './groups/groupId/invites.js';
106
108
  import { pathItems as groupsGroupIdMembers } from './groups/groupId/members.js';
107
109
  import { pathItems as groupsGroupId } from './groups/groupId.js';
@@ -170,6 +172,7 @@ export const paths = {
170
172
  '/api/apps/{appId}/export': appsAppIdExport,
171
173
  '/api/apps/{appId}/groups': appsAppIdGroups,
172
174
  '/api/apps/{appId}/icon': appsAppIdIcon,
175
+ '/api/apps/{appId}/invites/resend': appsAppIdInvitesResend,
173
176
  '/api/apps/{appId}/invites': appsAppIdInvites,
174
177
  '/api/apps/{appId}/lock': appsAppIdLock,
175
178
  '/api/apps/{appId}/maskable-icon': appsAppIdMaskableIcon,
@@ -270,6 +273,7 @@ export const paths = {
270
273
  '/api/group-invites/{token}': groupInvitesToken,
271
274
  '/api/group-members/{groupMemberId}/role': groupsMembersGroupMemberIdRole,
272
275
  '/api/group-members/{groupMemberId}': groupsMembersGroupMemberId,
276
+ '/api/groups/{groupId}/invites/resend': groupsGroupIdInvitesResend,
273
277
  '/api/groups/{groupId}/invites': groupsGroupIdInvites,
274
278
  '/api/groups/{groupId}/members': groupsGroupIdMembers,
275
279
  '/api/groups/{groupId}': groupsGroupId,
@@ -0,0 +1,2 @@
1
+ import { type OpenAPIV3 } from 'openapi-types';
2
+ export declare const paths: OpenAPIV3.PathsObject;
@@ -0,0 +1,31 @@
1
+ export const paths = {
2
+ '/api/apps/{appId}/resources/{resourceType}/{resourceId}/history': {
3
+ parameters: [
4
+ { $ref: '#/components/parameters/appId' },
5
+ { $ref: '#/components/parameters/resourceType' },
6
+ { $ref: '#/components/parameters/resourceId' },
7
+ ],
8
+ get: {
9
+ tags: ['resource'],
10
+ description: 'Get the known history of a resource',
11
+ operationId: 'getResourceHistory',
12
+ responses: {
13
+ 200: {
14
+ description: 'The resource that matches the given id.',
15
+ content: {
16
+ 'application/json': {
17
+ schema: {
18
+ type: 'array',
19
+ items: {
20
+ $ref: '#/components/responses/resource',
21
+ },
22
+ },
23
+ },
24
+ },
25
+ },
26
+ },
27
+ security: [{ studio: [] }, { app: ['resources:manage'] }],
28
+ },
29
+ },
30
+ };
31
+ //# sourceMappingURL=resourceHistory.js.map
@@ -9,11 +9,11 @@ export const pathItems = {
9
9
  in: 'query',
10
10
  },
11
11
  ],
12
- description: 'Get all apps that are editable by the user.',
12
+ description: 'Get all apps from organizations that the user is in.',
13
13
  operationId: 'queryCurrentUserApps',
14
14
  responses: {
15
15
  200: {
16
- description: 'The list of all editable apps.',
16
+ description: 'The list of all apps the user is in.',
17
17
  content: {
18
18
  'application/json': {
19
19
  schema: {
package/authorization.js CHANGED
@@ -1,6 +1,6 @@
1
- import { appOrganizationPermissionMapping, AppPermission, PredefinedAppRole, predefinedAppRolePermissions, predefinedOrganizationRolePermissions, } from '@appsemble/types';
1
+ import { appOrganizationPermissionMapping, AppPermission, predefinedAppRolePermissions, predefinedOrganizationRolePermissions, } from '@appsemble/types';
2
2
  function checkAppPermissions(acquiredPermissions, requiredPermissions) {
3
- const customAppResourcePermissionPattern = /^\$resource:[^:]+:(get|query|create|delete|patch|update)$/;
3
+ const customAppResourcePermissionPattern = /^\$resource:[^:]+:(get|history:get|query|create|delete|patch|update)$/;
4
4
  const customAppOwnResourcePermissionPattern = /^\$resource:[^:]+:own:(get|query|delete|patch|update)$/;
5
5
  const customAppResourceViewPermissionPattern = /^\$resource:[^:]+:(get|query):[^:]+$/;
6
6
  return requiredPermissions.every((p) => {
@@ -76,10 +76,7 @@ export function checkGuestAppPermissions(appSecurityDefinition, requiredPermissi
76
76
  return checkAppPermissions(guestPermissions, requiredPermissions);
77
77
  }
78
78
  export function getAppRoles(appSecurityDefinition) {
79
- return Array.from(new Set([
80
- ...Object.keys((appSecurityDefinition === null || appSecurityDefinition === void 0 ? void 0 : appSecurityDefinition.roles) || {}),
81
- ...Object.keys(PredefinedAppRole),
82
- ]));
79
+ return Array.from(new Set(Object.keys((appSecurityDefinition === null || appSecurityDefinition === void 0 ? void 0 : appSecurityDefinition.roles) || {})));
83
80
  }
84
81
  export function getAppPossibleGuestPermissions(appDefinition) {
85
82
  const possibleAllViews = {};
@@ -105,6 +102,7 @@ export function getAppPossibleGuestPermissions(appDefinition) {
105
102
  `$resource:${resourceName}:create`,
106
103
  `$resource:${resourceName}:query`,
107
104
  `$resource:${resourceName}:get`,
105
+ `$resource:${resourceName}:history:get`,
108
106
  `$resource:${resourceName}:update`,
109
107
  `$resource:${resourceName}:patch`,
110
108
  `$resource:${resourceName}:delete`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appsemble/utils",
3
- "version": "0.30.2",
3
+ "version": "0.30.8",
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.2",
40
+ "@appsemble/types": "0.30.8",
41
41
  "axios": "^1.0.0",
42
42
  "cron-parser": "^4.0.0",
43
43
  "date-fns": "^2.0.0",
package/remap.js CHANGED
@@ -319,7 +319,10 @@ const mapperImplementations = {
319
319
  }
320
320
  }
321
321
  else if (typeof prop === 'number' || typeof prop === 'string') {
322
- result = result[prop];
322
+ result =
323
+ Array.isArray(result) && typeof prop === 'number' && prop < 0
324
+ ? result[result.length + prop]
325
+ : result[prop];
323
326
  }
324
327
  return result;
325
328
  },
package/remap.test.js CHANGED
@@ -1089,6 +1089,16 @@ describe('prop', () => {
1089
1089
  mappers: { prop: 'foo' },
1090
1090
  expected: null,
1091
1091
  },
1092
+ 'handle array input': {
1093
+ input: ['one', 'two', 'three', 'four'],
1094
+ mappers: { prop: 1 },
1095
+ expected: 'two',
1096
+ },
1097
+ 'handle array input and negative index': {
1098
+ input: ['one', 'two', 'three', 'four'],
1099
+ mappers: { prop: -3 },
1100
+ expected: 'two',
1101
+ },
1092
1102
  });
1093
1103
  });
1094
1104
  describe('random.choice', () => {
package/validation.js CHANGED
@@ -7,8 +7,8 @@ import { has } from './has.js';
7
7
  import { findPageByName, getAppInheritedRoles, getAppPossibleGuestPermissions, getAppPossiblePermissions, getAppRolePermissions, normalize, partialNormalized, } from './index.js';
8
8
  import { iterApp } from './iterApp.js';
9
9
  import { serverActions } from './serverActions.js';
10
- const allResourcePermissionPattern = /^\$resource:all:(get|query|create|delete|patch|update)$/;
11
- const resourcePermissionPattern = /^\$resource:[^:]+:(get|query|create|delete|patch|update)$/;
10
+ const allResourcePermissionPattern = /^\$resource:all:(get|history:get|query|create|delete|patch|update)$/;
11
+ const resourcePermissionPattern = /^\$resource:[^:]+:(get|history:get|query|create|delete|patch|update)$/;
12
12
  const allOwnResourcePermissionPattern = /^\$resource:all:own:(get|query|delete|patch|update)$/;
13
13
  const ownResourcePermissionPattern = /^\$resource:[^:]+:own:(get|query|delete|patch|update)$/;
14
14
  const allResourceViewPermissionPattern = /^\$resource:all:(get|query):[^:]+$/;
@@ -1150,6 +1150,7 @@ export async function validateAppDefinition(definition, getBlockVersions, contro
1150
1150
  result = validator.validate(definition, {});
1151
1151
  }
1152
1152
  if (!definition) {
1153
+ result.addError('App definition can not be null');
1153
1154
  return result;
1154
1155
  }
1155
1156
  const blocks = getAppBlocks(definition);
@@ -2001,20 +2001,26 @@ describe('validateAppDefinition', () => {
2001
2001
  new ValidationError('there is no-one in the app, who has permissions to use this action', 'resource.get', undefined, ['controller', 'actions', 'onWhatever', 'resource']),
2002
2002
  ]);
2003
2003
  });
2004
- it('should ignore if an app is null', async () => {
2004
+ it('should throw if an app is null', async () => {
2005
2005
  const result = await validateAppDefinition(null, () => []);
2006
- expect(result.valid).toBe(true);
2007
- expect(result.errors).toStrictEqual([]);
2008
- });
2009
- it('should if app pages are not an array', async () => {
2010
- const result = await validateAppDefinition(null, () => []);
2011
- expect(result.valid).toBe(true);
2012
- expect(result.errors).toStrictEqual([]);
2006
+ expect(result.valid).toBe(false);
2007
+ expect(result.errors).toStrictEqual([
2008
+ expect.objectContaining({
2009
+ message: 'App definition can not be null',
2010
+ instance: null,
2011
+ schema: {},
2012
+ }),
2013
+ ]);
2013
2014
  });
2014
- it('should report an error if app validation fails for an unexpected reason', async () => {
2015
- const result = await validateAppDefinition(null, () => []);
2016
- expect(result.valid).toBe(true);
2017
- expect(result.errors).toStrictEqual([]);
2015
+ it('should report an error if the defaultPage does not exist', async () => {
2016
+ const result = await validateAppDefinition({ name: 'Test App', pages: [], defaultPage: 'Test Page' }, () => []);
2017
+ expect(result.valid).toBe(false);
2018
+ expect(result.errors).toStrictEqual([
2019
+ expect.objectContaining({
2020
+ instance: 'Test Page',
2021
+ message: 'does not refer to an existing page',
2022
+ }),
2023
+ ]);
2018
2024
  });
2019
2025
  it('should handle if an unexpected error occurs', async () => {
2020
2026
  const result = await validateAppDefinition({