@appsemble/utils 0.22.10 → 0.23.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.
Files changed (43) hide show
  1. package/README.md +3 -3
  2. package/api/components/parameters/index.d.ts +2 -0
  3. package/api/components/parameters/index.js +2 -0
  4. package/api/components/parameters/trainingBlockId.d.ts +2 -0
  5. package/api/components/parameters/trainingBlockId.js +8 -0
  6. package/api/components/parameters/trainingId.d.ts +2 -0
  7. package/api/components/parameters/trainingId.js +8 -0
  8. package/api/components/schemas/AppServiceSecret.js +1 -1
  9. package/api/components/schemas/ResourceDefinition.js +24 -4
  10. package/api/components/schemas/Training.d.ts +2 -0
  11. package/api/components/schemas/Training.js +32 -0
  12. package/api/components/schemas/TrainingBlock.d.ts +2 -0
  13. package/api/components/schemas/TrainingBlock.js +38 -0
  14. package/api/components/schemas/index.d.ts +2 -0
  15. package/api/components/schemas/index.js +2 -0
  16. package/api/components/securitySchemes/cli.test.js +1 -0
  17. package/api/index.test.js +1 -0
  18. package/api/paths/apps.js +50 -0
  19. package/api/paths/index.js +2 -0
  20. package/api/paths/trainings.d.ts +2 -0
  21. package/api/paths/trainings.js +279 -0
  22. package/appMessages.test.js +1 -0
  23. package/appSecurity.test.js +1 -0
  24. package/blockUtils.test.js +1 -0
  25. package/constants/patterns.test.js +1 -0
  26. package/constants/roles.js +3 -3
  27. package/convertToCsv.test.js +1 -0
  28. package/has.test.js +1 -0
  29. package/i18n.test.js +1 -0
  30. package/iterApp.test.js +1 -0
  31. package/jsonschema.test.js +1 -0
  32. package/mapValues.test.js +1 -0
  33. package/miscellaneous.test.js +1 -0
  34. package/normalize.test.js +1 -0
  35. package/objectCache.test.js +1 -0
  36. package/package.json +3 -2
  37. package/prefix.test.js +1 -0
  38. package/reference-schemas/remappers/arrays.js +29 -0
  39. package/remap.test.js +1 -0
  40. package/string.test.js +1 -0
  41. package/theme.test.js +1 -0
  42. package/validateStyle.test.js +1 -0
  43. package/validation.test.js +1 -0
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
- # ![](https://gitlab.com/appsemble/appsemble/-/raw/0.22.10/config/assets/logo.svg) Appsemble Utilities
1
+ # ![](https://gitlab.com/appsemble/appsemble/-/raw/0.23.0/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.22.10/pipeline.svg)](https://gitlab.com/appsemble/appsemble/-/releases/0.22.10)
6
+ [![GitLab CI](https://gitlab.com/appsemble/appsemble/badges/0.23.0/pipeline.svg)](https://gitlab.com/appsemble/appsemble/-/releases/0.23.0)
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.22.10/LICENSE.md) ©
29
+ [LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.23.0/LICENSE.md) ©
30
30
  [Appsemble](https://appsemble.com)
@@ -17,3 +17,5 @@ export * from './resourceType.js';
17
17
  export * from './screenshotId.js';
18
18
  export * from './appServiceId.js';
19
19
  export * from './view.js';
20
+ export * from './trainingId.js';
21
+ export * from './trainingBlockId.js';
@@ -17,4 +17,6 @@ export * from './resourceType.js';
17
17
  export * from './screenshotId.js';
18
18
  export * from './appServiceId.js';
19
19
  export * from './view.js';
20
+ export * from './trainingId.js';
21
+ export * from './trainingBlockId.js';
20
22
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,2 @@
1
+ import { type OpenAPIV3 } from 'openapi-types';
2
+ export declare const trainingBlockId: OpenAPIV3.ParameterObject;
@@ -0,0 +1,8 @@
1
+ export const trainingBlockId = {
2
+ name: 'trainingBlockId',
3
+ in: 'path',
4
+ description: 'Id of the trainingBlock on which the operation will be performed.',
5
+ required: true,
6
+ schema: { $ref: '#/components/schemas/TrainingBlock/properties/id' },
7
+ };
8
+ //# sourceMappingURL=trainingBlockId.js.map
@@ -0,0 +1,2 @@
1
+ import { type OpenAPIV3 } from 'openapi-types';
2
+ export declare const trainingId: OpenAPIV3.ParameterObject;
@@ -0,0 +1,8 @@
1
+ export const trainingId = {
2
+ name: 'trainingId',
3
+ in: 'path',
4
+ description: 'Id of the training on which the operation will be performed.',
5
+ required: true,
6
+ schema: { $ref: '#/components/schemas/Training/properties/id' },
7
+ };
8
+ //# sourceMappingURL=trainingId.js.map
@@ -9,7 +9,7 @@ export const AppServiceSecret = {
9
9
  description: 'An autogenerated ID.',
10
10
  readOnly: true,
11
11
  },
12
- serviceName: {
12
+ name: {
13
13
  type: 'string',
14
14
  description: 'An optional name to give extra clarity what the secret is used for.',
15
15
  },
@@ -13,15 +13,35 @@ const query = {
13
13
  description: 'The query parameters to use in the request.',
14
14
  additionalProperties: { type: 'string' },
15
15
  };
16
+ const referenceActionTrigger = {
17
+ type: 'object',
18
+ additionalProperties: false,
19
+ description: 'Defines the type of trigger and the cascading strategy for it',
20
+ properties: {
21
+ type: {
22
+ enum: ['create', 'update', 'delete'],
23
+ },
24
+ cascade: {
25
+ description: `Defines the cascading strategy.
26
+
27
+ If 'update' is specified, the referencing property of the referencing resource is set to null.
28
+
29
+ If 'delete' is specified, the referencing resource is deleted.
30
+
31
+ If not specified, the referenced resource cannot be deleted
32
+ without deleting the referencing resource first.`,
33
+ enum: ['update', 'delete'],
34
+ },
35
+ },
36
+ };
16
37
  const referenceAction = {
17
38
  type: 'object',
18
39
  additionalProperties: false,
19
- // XXX
20
- description: 'To be documented.',
40
+ description: 'Defines what happens when the specified action is executed on the referenced resource.',
21
41
  properties: {
22
- trigger: {
42
+ triggers: {
23
43
  type: 'array',
24
- items: { enum: ['create', 'update', 'delete'] },
44
+ items: referenceActionTrigger,
25
45
  minItems: 1,
26
46
  uniqueItems: true,
27
47
  },
@@ -0,0 +1,2 @@
1
+ import { type OpenAPIV3 } from 'openapi-types';
2
+ export declare const Training: OpenAPIV3.NonArraySchemaObject;
@@ -0,0 +1,32 @@
1
+ export const Training = {
2
+ type: 'object',
3
+ description: 'Object representation of a training',
4
+ additionalProperties: false,
5
+ properties: {
6
+ id: {
7
+ type: 'number',
8
+ readOnly: true,
9
+ minimum: 0,
10
+ description: 'The id of the training, will be generated automatically by the system.',
11
+ },
12
+ title: {
13
+ type: 'string',
14
+ description: 'Title of the training.',
15
+ },
16
+ description: {
17
+ type: 'string',
18
+ description: 'A brief overview of the training.',
19
+ },
20
+ competence: {
21
+ type: 'string',
22
+ description: 'Tag for the training',
23
+ },
24
+ difficultyLevel: {
25
+ type: 'number',
26
+ description: 'Difficulty level between 1 and 5',
27
+ minimum: 1,
28
+ maximum: 5,
29
+ },
30
+ },
31
+ };
32
+ //# sourceMappingURL=Training.js.map
@@ -0,0 +1,2 @@
1
+ import { type OpenAPIV3 } from 'openapi-types';
2
+ export declare const TrainingBlock: OpenAPIV3.NonArraySchemaObject;
@@ -0,0 +1,38 @@
1
+ export const TrainingBlock = {
2
+ type: 'object',
3
+ description: '',
4
+ additionalProperties: false,
5
+ properties: {
6
+ id: {
7
+ type: 'string',
8
+ description: 'The id of the Training Block, will be generated automatically by the system.',
9
+ readOnly: true,
10
+ },
11
+ trainingId: {
12
+ type: 'number',
13
+ minimum: 0,
14
+ description: 'Id of the parent training.',
15
+ },
16
+ title: {
17
+ type: 'string',
18
+ description: 'Title of the training block.',
19
+ },
20
+ documentationLink: {
21
+ type: 'string',
22
+ description: 'A URL pointing to a page in the documentation.',
23
+ },
24
+ videoLink: {
25
+ type: 'string',
26
+ description: 'Link of the video associated with the training block.',
27
+ },
28
+ exampleCode: {
29
+ type: 'string',
30
+ description: 'Example code for the user to be copied.',
31
+ },
32
+ externalResource: {
33
+ type: 'string',
34
+ description: 'Link to any external resource.',
35
+ },
36
+ },
37
+ };
38
+ //# sourceMappingURL=TrainingBlock.js.map
@@ -113,6 +113,8 @@ export * from './TeamsDefinition.js';
113
113
  export * from './Theme.js';
114
114
  export * from './ThrowActionDefinition.js';
115
115
  export * from './User.js';
116
+ export * from './Training.js';
117
+ export * from './TrainingBlock.js';
116
118
  export * from './UserEmail.js';
117
119
  export * from './UserLogoutActionDefinition.js';
118
120
  export * from './UserLoginActionDefinition.js';
@@ -113,6 +113,8 @@ export * from './TeamsDefinition.js';
113
113
  export * from './Theme.js';
114
114
  export * from './ThrowActionDefinition.js';
115
115
  export * from './User.js';
116
+ export * from './Training.js';
117
+ export * from './TrainingBlock.js';
116
118
  export * from './UserEmail.js';
117
119
  export * from './UserLogoutActionDefinition.js';
118
120
  export * from './UserLoginActionDefinition.js';
@@ -1,3 +1,4 @@
1
+ import { expect, it } from 'vitest';
1
2
  import { cli } from './cli.js';
2
3
  import { scopes } from '../../../constants/index.js';
3
4
  it('should match the known scopes defined in utils', () => {
package/api/index.test.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { readdirSync } from 'node:fs';
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import { createValidator } from 'koas-core/lib/validation.js';
4
+ import { describe, expect, it } from 'vitest';
4
5
  import { parse } from 'yaml';
5
6
  import { api, schemas } from './index.js';
6
7
  describe('schemas', () => {
package/api/paths/apps.js CHANGED
@@ -275,6 +275,53 @@ export const paths = {
275
275
  security: [{ studio: [] }],
276
276
  },
277
277
  },
278
+ '/api/apps/import/organization/{organizationId}': {
279
+ parameters: [{ $ref: '#/components/parameters/organizationId' }],
280
+ post: {
281
+ tags: ['app', 'import', 'zip'],
282
+ description: 'Import an app from a zip file',
283
+ operationId: 'importApp',
284
+ requestBody: {
285
+ content: {
286
+ 'application/zip': {
287
+ schema: {
288
+ type: 'string',
289
+ format: 'binary',
290
+ },
291
+ },
292
+ },
293
+ },
294
+ responses: {
295
+ 200: {
296
+ description: 'App imported successfully',
297
+ $ref: '#/components/responses/app',
298
+ },
299
+ },
300
+ security: [{ studio: [] }],
301
+ },
302
+ },
303
+ '/api/apps/{appId}/export': {
304
+ parameters: [{ $ref: '#/components/parameters/appId' }],
305
+ get: {
306
+ tags: ['app', 'export', 'zip'],
307
+ description: 'Export the app',
308
+ operationId: 'exportApp',
309
+ parameters: [
310
+ {
311
+ name: 'resources',
312
+ schema: { type: 'boolean' },
313
+ description: 'Whether to include resources for an app.',
314
+ in: 'query',
315
+ },
316
+ ],
317
+ responses: {
318
+ 200: {
319
+ description: 'App exported successfully.',
320
+ },
321
+ },
322
+ security: [{ studio: [] }],
323
+ },
324
+ },
278
325
  '/api/apps/{appId}/lock': {
279
326
  parameters: [{ $ref: '#/components/parameters/appId' }],
280
327
  post: {
@@ -301,6 +348,9 @@ export const paths = {
301
348
  responses: {
302
349
  204: {
303
350
  description: 'Lock status successfully changed',
351
+ content: {
352
+ 'application/zip': {},
353
+ },
304
354
  },
305
355
  },
306
356
  security: [{ studio: [] }, { cli: ['apps:write'] }],
@@ -23,6 +23,7 @@ import { paths as resourceHistory } from './resourceHistory.js';
23
23
  import { paths as resources } from './resources.js';
24
24
  import { paths as saml } from './saml.js';
25
25
  import { paths as templates } from './templates.js';
26
+ import { paths as trainings } from './trainings.js';
26
27
  import { paths as user } from './user.js';
27
28
  export const paths = {
28
29
  ...appOAuth2Secrets,
@@ -51,5 +52,6 @@ export const paths = {
51
52
  ...translations,
52
53
  ...appServiceSecrets,
53
54
  ...user,
55
+ ...trainings,
54
56
  };
55
57
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,2 @@
1
+ import { type OpenAPIV3 } from 'openapi-types';
2
+ export declare const paths: OpenAPIV3.PathsObject;
@@ -0,0 +1,279 @@
1
+ export const paths = {
2
+ '/api/trainings': {
3
+ get: {
4
+ tags: ['training', 'learning', 'docs'],
5
+ description: 'Fetch all trainings available',
6
+ operationId: 'getTrainings',
7
+ responses: {
8
+ 200: {
9
+ description: 'An array of all the available trainings.',
10
+ content: {
11
+ 'application/json': {
12
+ schema: {
13
+ type: 'array',
14
+ items: { $ref: '#/components/schemas/Training' },
15
+ },
16
+ },
17
+ },
18
+ },
19
+ },
20
+ security: [{ studio: [] }],
21
+ },
22
+ post: {
23
+ tags: ['training', 'learning', 'docs'],
24
+ description: '',
25
+ operationId: 'createTraining',
26
+ requestBody: {
27
+ required: true,
28
+ content: {
29
+ 'application/json': {
30
+ schema: {
31
+ $ref: '#/components/schemas/Training',
32
+ },
33
+ },
34
+ },
35
+ },
36
+ responses: {
37
+ 201: {
38
+ description: 'Created new training successfully',
39
+ $ref: '#/components/schemas/Training',
40
+ },
41
+ },
42
+ security: [{ studio: [] }],
43
+ },
44
+ },
45
+ '/api/trainings/{trainingId}': {
46
+ parameters: [{ $ref: '#/components/parameters/trainingId' }],
47
+ get: {
48
+ tags: ['training', 'learning', 'docs'],
49
+ description: 'Fetch a single training by id.',
50
+ operationId: 'getTrainingById',
51
+ responses: {
52
+ 200: {
53
+ description: 'Object representation of a training',
54
+ content: {
55
+ 'application/json': {
56
+ schema: {
57
+ $ref: '#/components/schemas/Training',
58
+ },
59
+ },
60
+ },
61
+ },
62
+ },
63
+ security: [{ studio: [] }],
64
+ },
65
+ patch: {
66
+ tags: ['training', 'learning', 'docs'],
67
+ description: 'Fetch a single training by id.',
68
+ operationId: 'patchTraining',
69
+ requestBody: {
70
+ required: true,
71
+ content: {
72
+ 'application/json': {
73
+ schema: {
74
+ $ref: '#/components/schemas/Training',
75
+ },
76
+ },
77
+ },
78
+ },
79
+ responses: {
80
+ 200: {
81
+ description: 'Object representation of a training',
82
+ content: {
83
+ 'application/json': {
84
+ schema: {
85
+ $ref: '#/components/schemas/Training',
86
+ },
87
+ },
88
+ },
89
+ },
90
+ },
91
+ security: [{ studio: [] }],
92
+ },
93
+ delete: {
94
+ tags: ['delete', 'training'],
95
+ description: 'Delete a training by Id.',
96
+ operationId: 'deleteTraining',
97
+ responses: {
98
+ 204: { description: 'Deleted the specified training.' },
99
+ },
100
+ security: [{ studio: [] }],
101
+ },
102
+ },
103
+ '/api/trainings/{trainingId}/blocks': {
104
+ parameters: [{ $ref: '#/components/parameters/trainingId' }],
105
+ get: {
106
+ tags: ['training', 'trainingBlocks', 'learning', 'docs'],
107
+ description: 'Fetch all training blocks by training id',
108
+ operationId: 'getTrainingBlocksByTrainingId',
109
+ responses: {
110
+ 200: {
111
+ description: 'An array of all the training Blocks associated with a training.',
112
+ content: {
113
+ 'application/json': {
114
+ schema: {
115
+ type: 'array',
116
+ items: { $ref: '#/components/schemas/TrainingBlock' },
117
+ },
118
+ },
119
+ },
120
+ },
121
+ },
122
+ security: [{ studio: [] }],
123
+ },
124
+ post: {
125
+ tags: ['training', 'trainingBlocks', 'learning', 'docs'],
126
+ description: 'Create a new training block as a child of a training.',
127
+ operationId: 'createTrainingBlock',
128
+ requestBody: {
129
+ required: true,
130
+ content: {
131
+ 'multipart/form-data': {
132
+ schema: {
133
+ type: 'object',
134
+ properties: {
135
+ documentationLink: {
136
+ $ref: '#/components/schemas/TrainingBlock/properties/documentationLink',
137
+ },
138
+ videoLink: { $ref: '#/components/schemas/TrainingBlock/properties/videoLink' },
139
+ exampleCode: { $ref: '#/components/schemas/TrainingBlock/properties/exampleCode' },
140
+ title: { $ref: '#/components/schemas/TrainingBlock/properties/title' },
141
+ externalResource: {
142
+ $ref: '#/components/schemas/TrainingBlock/properties/externalResource',
143
+ },
144
+ },
145
+ },
146
+ },
147
+ },
148
+ },
149
+ responses: { 201: { description: 'Created new training block successfully' } },
150
+ security: [{ studio: [] }],
151
+ },
152
+ },
153
+ '/api/trainings/{trainingId}/enroll/users': {
154
+ parameters: [{ $ref: '#/components/parameters/trainingId' }],
155
+ get: {
156
+ tags: ['training', 'users', 'enrolled'],
157
+ description: 'Get a list of all users who have completed a training.',
158
+ operationId: 'getTrainedUsers',
159
+ responses: {
160
+ 200: {
161
+ description: 'A list of all the users who have completed a training.',
162
+ content: {
163
+ 'application/json': {
164
+ schema: {
165
+ type: 'array',
166
+ items: {
167
+ $ref: '#/components/schemas/User',
168
+ },
169
+ },
170
+ },
171
+ },
172
+ },
173
+ },
174
+ security: [{ studio: [] }],
175
+ },
176
+ },
177
+ '/api/trainings/{trainingId}/enroll': {
178
+ parameters: [{ $ref: '#/components/parameters/trainingId' }],
179
+ get: {
180
+ tags: ['training', 'learning', 'user'],
181
+ description: 'Check if a user is enrolled in a training.',
182
+ operationId: 'isUserEnrolled',
183
+ responses: {
184
+ 200: {
185
+ description: 'A boolean returning whether a user is enrolled in a training',
186
+ content: {
187
+ 'application/json': {
188
+ schema: {
189
+ type: 'object',
190
+ properties: {
191
+ enrolled: {
192
+ type: 'boolean',
193
+ description: 'If a user is enrolled in a training',
194
+ },
195
+ completed: {
196
+ type: 'boolean',
197
+ description: 'If the training has been completed by the user.',
198
+ },
199
+ },
200
+ },
201
+ },
202
+ },
203
+ },
204
+ },
205
+ security: [{ studio: [] }],
206
+ },
207
+ post: {
208
+ tags: ['training', 'learning', 'user'],
209
+ description: 'Enroll a user in a training.',
210
+ operationId: 'enrollUserInTraining',
211
+ responses: { 201: { description: 'Enrolled in the training successfully' } },
212
+ security: [{ studio: [] }],
213
+ },
214
+ patch: {
215
+ tags: ['training', 'learning', 'user'],
216
+ description: 'Edit an enrollment of a user in a training',
217
+ operationId: 'updateTrainingCompletionStatus',
218
+ requestBody: {
219
+ required: true,
220
+ content: {
221
+ 'multipart/form-data': {
222
+ schema: {
223
+ type: 'object',
224
+ properties: {
225
+ completed: {
226
+ type: 'boolean',
227
+ description: 'If the training has been completed by the user.',
228
+ },
229
+ },
230
+ },
231
+ },
232
+ },
233
+ },
234
+ responses: { 200: { description: 'Updated user training successfully.' } },
235
+ security: [{ studio: [] }],
236
+ },
237
+ },
238
+ '/api/training/blocks/{trainingBlockId}': {
239
+ parameters: [{ $ref: '#/components/parameters/trainingBlockId' }],
240
+ patch: {
241
+ tags: ['training', 'trainingBlocks', 'learning', 'docs'],
242
+ description: 'Update a training block with new content',
243
+ operationId: 'patchTrainingBlock',
244
+ requestBody: {
245
+ required: true,
246
+ content: {
247
+ 'multipart/form-data': {
248
+ schema: {
249
+ type: 'object',
250
+ properties: {
251
+ documentationLink: {
252
+ $ref: '#/components/schemas/TrainingBlock/properties/documentationLink',
253
+ },
254
+ videoLink: { $ref: '#/components/schemas/TrainingBlock/properties/videoLink' },
255
+ exampleCode: { $ref: '#/components/schemas/TrainingBlock/properties/exampleCode' },
256
+ title: { $ref: '#/components/schemas/TrainingBlock/properties/title' },
257
+ externalResource: {
258
+ $ref: '#/components/schemas/TrainingBlock/properties/externalResource',
259
+ },
260
+ },
261
+ },
262
+ },
263
+ },
264
+ },
265
+ responses: { 200: { $ref: '#/components/schemas/TrainingBlock' } },
266
+ security: [{ studio: [] }],
267
+ },
268
+ delete: {
269
+ tags: ['delete', 'training'],
270
+ description: 'Delete a training block by Id.',
271
+ operationId: 'deleteTrainingBlock',
272
+ responses: {
273
+ 204: { description: 'Deleted the specified training block.' },
274
+ },
275
+ security: [{ studio: [] }],
276
+ },
277
+ },
278
+ };
279
+ //# sourceMappingURL=trainings.js.map
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it, vi } from 'vitest';
1
2
  import { extractAppMessages, findMessageIds } from './appMessages.js';
2
3
  describe('findMessageIds', () => {
3
4
  it('should ignore null', () => {
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { resolveRoleInheritance } from './appSecurity.js';
2
3
  describe('resolveRoleInheritance', () => {
3
4
  it('should return an empty array if no security definition is defined', () => {
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { getAppBlocks, normalizeBlockName, parseBlockName, prefixBlockURL, stripBlockName, } from './blockUtils.js';
2
3
  describe('normalizeBlockName', () => {
3
4
  it('should prepend @appsemble if no organization is prepended', () => {
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { domainPattern, googleAnalyticsIDPattern, normalized, partialNormalized, } from './patterns.js';
2
3
  describe('partialNormalized', () => {
3
4
  it.each([
@@ -22,6 +22,9 @@ const Maintainer = [
22
22
  Permission.ManageTeams,
23
23
  Permission.PublishBlocks,
24
24
  Permission.DeleteBlocks,
25
+ Permission.CreateCollections,
26
+ Permission.DeleteCollections,
27
+ Permission.EditCollections,
25
28
  ];
26
29
  const Owner = [
27
30
  ...Maintainer,
@@ -29,9 +32,6 @@ const Owner = [
29
32
  Permission.DeleteOrganization,
30
33
  Permission.ManageMembers,
31
34
  Permission.ManageRoles,
32
- Permission.CreateCollections,
33
- Permission.DeleteCollections,
34
- Permission.EditCollections,
35
35
  ];
36
36
  export const roles = {
37
37
  Member: member,
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { convertToCsv } from './convertToCsv.js';
2
3
  describe('convertToCsv', () => {
3
4
  it('should throw an error if input is null', () => {
package/has.test.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { has } from './has.js';
2
3
  describe('has', () => {
3
4
  it('should return false for null targets', () => {
package/i18n.test.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { detectLocale, sortLocales } from './i18n.js';
2
3
  describe('sortLocales', () => {
3
4
  const tests = [
package/iterApp.test.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it, vi } from 'vitest';
1
2
  import { iterAction, iterApp, iterBlock, iterBlockList, iterPage } from './iterApp.js';
2
3
  describe('iterAction', () => {
3
4
  it('should call the appropriate callbacks', () => {
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it, vi } from 'vitest';
1
2
  import { combineSchemas, generateDataFromSchema, iterJSONSchema } from './jsonschema.js';
2
3
  describe('generateDataFromSchema', () => {
3
4
  it('should not crash if no schema is defined', () => {
package/mapValues.test.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { mapValues } from './mapValues.js';
2
3
  describe('mapValues', () => {
3
4
  it('should call the iteratee over each own property', () => {
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { identity, rethrow, stripNullValues } from './miscellaneous.js';
2
3
  /**
3
4
  * Return the input data.
package/normalize.test.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { expect, it } from 'vitest';
1
2
  import { normalize } from './normalize.js';
2
3
  const fixtures = [
3
4
  ['Foo', 'foo'],
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { objectCache } from './objectCache.js';
2
3
  describe('objectCache', () => {
3
4
  it('should cache results', () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appsemble/utils",
3
- "version": "0.22.10",
3
+ "version": "0.23.0",
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.22.10",
40
+ "@appsemble/types": "0.23.0",
41
41
  "axios": "^1.0.0",
42
42
  "cron-parser": "^4.0.0",
43
43
  "date-fns": "^2.0.0",
@@ -59,6 +59,7 @@
59
59
  "@types/lcm": "^0.0.0",
60
60
  "bulma": "0.9.3",
61
61
  "koas-core": "^0.7.0",
62
+ "vitest": "^0.34.0",
62
63
  "yaml": "^2.0.0"
63
64
  },
64
65
  "engines": {
package/prefix.test.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { expect, it } from 'vitest';
1
2
  import { prefix } from './prefix.js';
2
3
  it('should prefix a string if the prefix is truthy', () => {
3
4
  const result = prefix('bar', 'foo');
@@ -257,6 +257,35 @@ Result:
257
257
  }
258
258
  ]
259
259
  \`\`\`
260
+
261
+ Extra example:
262
+
263
+ array remapper can also be used in combination with different remappers as in the current example comes object remapper:
264
+
265
+ Example object:
266
+ \`\`\`json
267
+ {
268
+ "names": ["Bart", "Bas", "Sam"]
269
+ }
270
+ \`\`\`
271
+
272
+ Combination of object and array remapper:
273
+
274
+ \`\`\`yaml
275
+ object.assign:
276
+ names:
277
+ - { prop: Applicants }
278
+ - array.append:
279
+ - Kevin
280
+ \`\`\`
281
+
282
+ Result:
283
+ \`\`\`json
284
+ {
285
+ "names": ["Bart", "Bas", "Sam", "Kevin"]
286
+ }
287
+ \`\`\`
288
+ \
260
289
  `,
261
290
  },
262
291
  'array.omit': {
package/remap.test.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { IntlMessageFormat } from 'intl-messageformat';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
3
  import { remap } from './remap.js';
3
4
  function runTests(tests) {
4
5
  it.each(Object.entries(tests))('should %s', (name, { context, expected, history, input, mappers, messages, userInfo }) => {
package/string.test.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { camelToHyphen, decodeJSONRef, toUpperCase } from './string.js';
2
3
  describe('camelToHyphen', () => {
3
4
  it('should convert camel case to hyphenated', () => {
package/theme.test.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import bulmaPkg from 'bulma/package.json';
2
+ import { describe, expect, it } from 'vitest';
2
3
  import { createThemeURL, mergeThemes } from './theme.js';
3
4
  describe('mergeThemes', () => {
4
5
  it('should use the base theme as default', () => {
@@ -1,3 +1,4 @@
1
+ import { describe, expect, it } from 'vitest';
1
2
  import { StyleValidationError, validateStyle } from './validateStyle.js';
2
3
  describe('validateStyle', () => {
3
4
  it('should validate correct CSS', () => {
@@ -1,4 +1,5 @@
1
1
  import { ValidationError } from 'jsonschema';
2
+ import { describe, expect, it } from 'vitest';
2
3
  import { validateAppDefinition } from './validation.js';
3
4
  function createTestApp() {
4
5
  return {