@nymphjs/tilmeld 1.0.0-beta.8 → 1.0.0-beta.81

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 (57) hide show
  1. package/CHANGELOG.md +368 -0
  2. package/README.md +14 -1
  3. package/dist/AbleObject.d.ts +15 -0
  4. package/dist/AbleObject.js +16 -1
  5. package/dist/AbleObject.js.map +1 -1
  6. package/dist/Group.d.ts +130 -10
  7. package/dist/Group.js +311 -129
  8. package/dist/Group.js.map +1 -1
  9. package/dist/Group.test.js +77 -2
  10. package/dist/Group.test.js.map +1 -1
  11. package/dist/Tilmeld.d.ts +237 -9
  12. package/dist/Tilmeld.js +569 -201
  13. package/dist/Tilmeld.js.map +1 -1
  14. package/dist/User.d.ts +344 -16
  15. package/dist/User.js +979 -313
  16. package/dist/User.js.map +1 -1
  17. package/dist/User.test.js +63 -2
  18. package/dist/User.test.js.map +1 -1
  19. package/dist/conf/d.d.ts +250 -3
  20. package/dist/conf/defaults.js +146 -98
  21. package/dist/conf/defaults.js.map +1 -1
  22. package/dist/enforceTilmeld.d.ts +3 -0
  23. package/dist/enforceTilmeld.js +22 -0
  24. package/dist/enforceTilmeld.js.map +1 -0
  25. package/dist/errors/AccessControlError.d.ts +2 -1
  26. package/dist/errors/AccessControlError.js +3 -2
  27. package/dist/errors/AccessControlError.js.map +1 -1
  28. package/dist/errors/BadDataError.d.ts +2 -1
  29. package/dist/errors/BadDataError.js +3 -2
  30. package/dist/errors/BadDataError.js.map +1 -1
  31. package/dist/errors/BadEmailError.d.ts +2 -1
  32. package/dist/errors/BadEmailError.js +3 -2
  33. package/dist/errors/BadEmailError.js.map +1 -1
  34. package/dist/errors/BadUsernameError.d.ts +2 -1
  35. package/dist/errors/BadUsernameError.js +3 -2
  36. package/dist/errors/BadUsernameError.js.map +1 -1
  37. package/dist/index.d.ts +1 -0
  38. package/dist/index.js +1 -0
  39. package/dist/index.js.map +1 -1
  40. package/package.json +30 -26
  41. package/src/AbleObject.ts +2 -2
  42. package/src/Group.test.ts +102 -4
  43. package/src/Group.ts +274 -158
  44. package/src/Tilmeld.ts +383 -249
  45. package/src/User.test.ts +80 -4
  46. package/src/User.ts +1061 -382
  47. package/src/conf/d.ts +26 -13
  48. package/src/conf/defaults.ts +151 -104
  49. package/src/enforceTilmeld.ts +20 -0
  50. package/src/errors/AccessControlError.ts +4 -2
  51. package/src/errors/BadDataError.ts +4 -2
  52. package/src/errors/BadEmailError.ts +4 -2
  53. package/src/errors/BadUsernameError.ts +4 -2
  54. package/src/index.ts +2 -0
  55. package/src/tilmeldJoiProps.ts +9 -9
  56. package/tsconfig.json +3 -3
  57. package/typedoc.json +4 -0
package/src/conf/d.ts CHANGED
@@ -9,7 +9,7 @@ import User, { UserData } from '../User';
9
9
  */
10
10
  export interface Config {
11
11
  /**
12
- * The name of the app. Used in emails sent to users.
12
+ * The name of the app. Used in emails sent to users and as the TOTP issuer.
13
13
  */
14
14
  appName: string;
15
15
  /**
@@ -206,7 +206,11 @@ export interface Config {
206
206
  */
207
207
  validEmailRegexNotice: string;
208
208
  /**
209
- * The maximum length for usernames. Infinity for unlimited.
209
+ * The minimum length for usernames.
210
+ */
211
+ minUsernameLength: number;
212
+ /**
213
+ * The maximum length for usernames. `Infinity` for unlimited.
210
214
  */
211
215
  maxUsernameLength: number;
212
216
  /**
@@ -217,6 +221,11 @@ export interface Config {
217
221
  * How long from current time, in seconds, the JWT token expires.
218
222
  */
219
223
  jwtExpire: number;
224
+ /**
225
+ * How long from current time, in seconds, the JWT token for a user switch
226
+ * expires.
227
+ */
228
+ jwtSwitchExpire: number;
220
229
  /**
221
230
  * How long, in seconds, before the JWT token expires to give the user a new
222
231
  * one.
@@ -225,21 +234,25 @@ export interface Config {
225
234
  /**
226
235
  * Function to build the JWT for user sessions.
227
236
  */
228
- jwtBuilder: (config: Config, user: User) => string;
237
+ jwtBuilder: (
238
+ config: Config,
239
+ user: User & UserData,
240
+ switchToken?: boolean,
241
+ ) => string;
229
242
  /**
230
- * Function to verify that a JWT was signed with the secret key, vaildate its
231
- * data, validate the XSRF token, and extract the GUID.
243
+ * Function to verify that a JWT was signed with the secret key, validate its
244
+ * data, validate the XSRF token, and extract the GUID and dates.
232
245
  *
233
246
  * If no XSRF token is supplied, ignore it.
234
247
  *
235
- * Return false if the JWT is not valid, or an array of GUID and expire
236
- * timestamp otherwise.
248
+ * Return false if the JWT is not valid, or an object with GUID, issued date,
249
+ * and expire date otherwise.
237
250
  */
238
251
  jwtExtract: (
239
252
  config: Config,
240
253
  token: string,
241
- xsrfToken?: string
242
- ) => { guid: string; expire: Date } | null;
254
+ xsrfToken?: string,
255
+ ) => { guid: string; issued: Date; expire: Date } | null;
243
256
  /**
244
257
  * The absolute path to the email template directory. Used by the default
245
258
  * email sender.
@@ -255,7 +268,7 @@ export interface Config {
255
268
  configEmail: (
256
269
  tilmeld: Tilmeld,
257
270
  options: EmailOptions,
258
- user: User & UserData
271
+ user: User & UserData,
259
272
  ) => Promise<EmailConfig>;
260
273
  /**
261
274
  * Send an email to a user. Uses `email-templates` by default.
@@ -285,7 +298,7 @@ export interface Config {
285
298
  sendEmail: (
286
299
  tilmeld: Tilmeld,
287
300
  options: EmailOptions,
288
- user: User & UserData
301
+ user: User & UserData,
289
302
  ) => Promise<boolean>;
290
303
  /**
291
304
  * The address you'd like to receive a notification of registered users, if
@@ -295,9 +308,9 @@ export interface Config {
295
308
  /**
296
309
  * The validator used to check groups before saving.
297
310
  */
298
- validatorGroup: (group: Group & GroupData) => void;
311
+ validatorGroup: (tilmeld: Tilmeld, group: Group & GroupData) => void;
299
312
  /**
300
313
  * The validator used to check users before saving.
301
314
  */
302
- validatorUser: (user: User & UserData) => void;
315
+ validatorUser: (tilmeld: Tilmeld, user: User & UserData) => void;
303
316
  }
@@ -49,11 +49,13 @@ export default {
49
49
  validEmailRegex:
50
50
  /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,
51
51
  validEmailRegexNotice: 'Email must be a correctly formatted address.',
52
+ minUsernameLength: 1,
52
53
  maxUsernameLength: 128,
53
54
  jwtSecret: '',
54
- jwtExpire: 60 * 60 * 24 * 7 * 8, // 8 weeks(ish)
55
- jwtRenew: 60 * 60 * 24 * 7 * 2, // 2 weeks(ish)
56
- jwtBuilder: (config, user) => {
55
+ jwtExpire: 60 * 60 * 24 * 7 * 24, // 24 weeks(ish) (6 months)
56
+ jwtSwitchExpire: 60 * 60 * 6, // 6 hours
57
+ jwtRenew: 60 * 60 * 24 * 7 * 12, // 12 weeks(ish) (3 months)
58
+ jwtBuilder: (config, user, switchToken) => {
57
59
  const secret = config.jwtSecret;
58
60
  if (secret === '') {
59
61
  throw new Error('JWT secret is not configured.');
@@ -62,12 +64,14 @@ export default {
62
64
  return jwt.sign(
63
65
  {
64
66
  iss: config.appUrl,
65
- nbf: Date.now() / 1000,
67
+ nbf: Math.floor(Date.now() / 1000),
68
+ exp:
69
+ Math.floor(Date.now() / 1000) +
70
+ (switchToken ? config.jwtSwitchExpire : config.jwtExpire),
66
71
  guid: user.guid,
67
72
  xsrfToken: 'TILMELDXSRF-' + nanoid(),
68
73
  },
69
74
  secret,
70
- { expiresIn: config.jwtExpire }
71
75
  );
72
76
  },
73
77
  jwtExtract: (config, token, xsrfToken?) => {
@@ -79,14 +83,14 @@ export default {
79
83
  try {
80
84
  const payload = jwt.verify(token, secret, {
81
85
  issuer: config.appUrl,
82
- clockTolerance: 10,
86
+ clockTolerance: 60,
83
87
  });
84
88
 
85
89
  if (typeof payload === 'string') {
86
90
  return null;
87
91
  }
88
92
 
89
- const { guid, xsrfToken: jwtXsrfToken, exp } = payload;
93
+ const { guid, xsrfToken: jwtXsrfToken, exp, nbf } = payload;
90
94
 
91
95
  if (xsrfToken != null && xsrfToken !== jwtXsrfToken) {
92
96
  return null;
@@ -96,13 +100,19 @@ export default {
96
100
  return null;
97
101
  }
98
102
 
103
+ if (nbf == null) {
104
+ return null;
105
+ }
106
+
107
+ const issued = new Date(nbf * 1000);
108
+
99
109
  if (exp == null) {
100
110
  return null;
101
111
  }
102
112
 
103
113
  const expire = new Date(exp * 1000);
104
114
 
105
- return { guid, expire };
115
+ return { guid, issued, expire };
106
116
  } catch (e: any) {
107
117
  return null;
108
118
  }
@@ -121,14 +131,14 @@ export default {
121
131
  },
122
132
  sendEmail: async (tilmeld, options, user) => {
123
133
  const email = new Email(
124
- await tilmeld.config.configEmail(tilmeld, options, user)
134
+ await tilmeld.config.configEmail(tilmeld, options, user),
125
135
  );
126
136
  try {
127
137
  const result = await email.send({
128
138
  ...options,
129
139
  template: path.join(
130
140
  tilmeld.config.emailTemplateDir,
131
- options.template ?? '.'
141
+ options.template ?? '.',
132
142
  ),
133
143
  locals: {
134
144
  // System
@@ -156,29 +166,48 @@ export default {
156
166
  });
157
167
  return !!result;
158
168
  } catch (e: any) {
169
+ tilmeld.nymph.config.debugError('tilmeld', `Failed to send email: ${e}`);
159
170
  return false;
160
171
  }
161
172
  },
162
173
  userRegisteredRecipient: null,
163
- validatorGroup: (group) => {
174
+ validatorGroup: (tilmeld, group) => {
175
+ const nameFields = tilmeld.config.userFields.includes('name')
176
+ ? {
177
+ name: Joi.string()
178
+ .trim(false)
179
+ .pattern(/[\x01-\x1F\x7F]/, {
180
+ name: 'control characters',
181
+ invert: true,
182
+ })
183
+ .max(512)
184
+ .required(),
185
+ }
186
+ : {};
187
+ const emailFields = tilmeld.config.userFields.includes('email')
188
+ ? {
189
+ email: Joi.string()
190
+ .trim(false)
191
+ .email({ minDomainSegments: 1, tlds: false })
192
+ .required(),
193
+ }
194
+ : {};
195
+ const phoneFields = tilmeld.config.userFields.includes('phone')
196
+ ? {
197
+ phone: Joi.string()
198
+ .trim(false)
199
+ .pattern(/^[0-9]+$/, 'numbers'),
200
+ }
201
+ : {};
164
202
  Joi.attempt(
165
203
  group.$getValidatable(),
166
204
  Joi.object().keys({
167
205
  ...nymphJoiProps,
206
+ ...nameFields,
207
+ ...emailFields,
208
+ ...phoneFields,
168
209
  groupname: Joi.string().trim(false).required(),
169
210
  enabled: Joi.boolean().required(),
170
- email: Joi.string()
171
- .trim(false)
172
- .email({ minDomainSegments: 1, tlds: false })
173
- .required(),
174
- name: Joi.string()
175
- .trim(false)
176
- .pattern(/[\x01-\x1F\x7F]/, {
177
- name: 'control characters',
178
- invert: true,
179
- })
180
- .max(512)
181
- .required(),
182
211
  avatar: Joi.string()
183
212
  .trim(false)
184
213
  .uri()
@@ -187,9 +216,6 @@ export default {
187
216
  invert: true,
188
217
  })
189
218
  .max(1024),
190
- phone: Joi.string()
191
- .trim(false)
192
- .pattern(/^[0-9]+$/, 'numbers'),
193
219
  parent: Joi.object().instance(Group),
194
220
  user: Joi.object().instance(User),
195
221
  abilities: Joi.array().items(
@@ -198,56 +224,110 @@ export default {
198
224
  name: 'control characters',
199
225
  invert: true,
200
226
  })
201
- .max(256)
227
+ .max(256),
202
228
  ),
203
229
  defaultPrimary: Joi.boolean(),
204
230
  defaultSecondary: Joi.boolean(),
205
231
  unverifiedSecondary: Joi.boolean(),
206
232
  }),
207
- 'Invalid Group: '
233
+ 'Invalid Group: ',
208
234
  );
209
235
  },
210
- validatorUser: (user) => {
236
+ validatorUser: (tilmeld, user) => {
237
+ const nameFields = tilmeld.config.userFields.includes('name')
238
+ ? {
239
+ nameFirst: Joi.string()
240
+ .trim(false)
241
+ .pattern(/[\x01-\x1F\x7F]/, {
242
+ name: 'control characters',
243
+ invert: true,
244
+ })
245
+ .max(512)
246
+ .required(),
247
+ nameMiddle: Joi.string()
248
+ .trim(false)
249
+ .pattern(/[\x01-\x1F\x7F]/, {
250
+ name: 'control characters',
251
+ invert: true,
252
+ })
253
+ .max(512),
254
+ nameLast: Joi.string()
255
+ .trim(false)
256
+ .pattern(/[\x01-\x1F\x7F]/, {
257
+ name: 'control characters',
258
+ invert: true,
259
+ })
260
+ .max(512),
261
+ name: Joi.string()
262
+ .trim(false)
263
+ .pattern(/[\x01-\x1F\x7F]/, {
264
+ name: 'control characters',
265
+ invert: true,
266
+ })
267
+ .max(512)
268
+ .required(),
269
+ }
270
+ : {};
271
+ const emailFields = tilmeld.config.userFields.includes('email')
272
+ ? {
273
+ email: Joi.string()
274
+ .trim(false)
275
+ .email({ minDomainSegments: 1, tlds: false })
276
+ .required(),
277
+ secret: Joi.string()
278
+ .trim(false)
279
+ .pattern(/[\x01-\x1F\x7F]/, {
280
+ name: 'control characters',
281
+ invert: true,
282
+ })
283
+ .length(21),
284
+ emailChangeDate: Joi.number(),
285
+ newEmailSecret: Joi.string()
286
+ .trim(false)
287
+ .pattern(/[\x01-\x1F\x7F]/, {
288
+ name: 'control characters',
289
+ invert: true,
290
+ })
291
+ .length(21),
292
+ newEmailAddress: Joi.string()
293
+ .trim(false)
294
+ .email({ minDomainSegments: 1, tlds: false }),
295
+ cancelEmailSecret: Joi.string()
296
+ .trim(false)
297
+ .pattern(/[\x01-\x1F\x7F]/, {
298
+ name: 'control characters',
299
+ invert: true,
300
+ })
301
+ .length(21),
302
+ cancelEmailAddress: Joi.string()
303
+ .trim(false)
304
+ .email({ minDomainSegments: 1, tlds: false }),
305
+ recoverSecret: Joi.string()
306
+ .trim(false)
307
+ .pattern(/[\x01-\x1F\x7F]/, {
308
+ name: 'control characters',
309
+ invert: true,
310
+ })
311
+ .length(10),
312
+ recoverSecretDate: Joi.number(),
313
+ }
314
+ : {};
315
+ const phoneFields = tilmeld.config.userFields.includes('phone')
316
+ ? {
317
+ phone: Joi.string()
318
+ .trim(false)
319
+ .pattern(/^[0-9]+$/, 'numbers'),
320
+ }
321
+ : {};
211
322
  Joi.attempt(
212
323
  user.$getValidatable(),
213
324
  Joi.object().keys({
214
325
  ...nymphJoiProps,
326
+ ...nameFields,
327
+ ...emailFields,
328
+ ...phoneFields,
215
329
  username: Joi.string().trim(false).required(),
216
330
  enabled: Joi.boolean().required(),
217
- email: Joi.string()
218
- .trim(false)
219
- .email({ minDomainSegments: 1, tlds: false })
220
- .required(),
221
- nameFirst: Joi.string()
222
- .trim(false)
223
- .pattern(/[\x01-\x1F\x7F]/, {
224
- name: 'control characters',
225
- invert: true,
226
- })
227
- .max(512)
228
- .required(),
229
- nameMiddle: Joi.string()
230
- .trim(false)
231
- .pattern(/[\x01-\x1F\x7F]/, {
232
- name: 'control characters',
233
- invert: true,
234
- })
235
- .max(512),
236
- nameLast: Joi.string()
237
- .trim(false)
238
- .pattern(/[\x01-\x1F\x7F]/, {
239
- name: 'control characters',
240
- invert: true,
241
- })
242
- .max(512),
243
- name: Joi.string()
244
- .trim(false)
245
- .pattern(/[\x01-\x1F\x7F]/, {
246
- name: 'control characters',
247
- invert: true,
248
- })
249
- .max(512)
250
- .required(),
251
331
  avatar: Joi.string()
252
332
  .trim(false)
253
333
  .uri()
@@ -256,9 +336,6 @@ export default {
256
336
  invert: true,
257
337
  })
258
338
  .max(1024),
259
- phone: Joi.string()
260
- .trim(false)
261
- .pattern(/^[0-9]+$/, 'numbers'),
262
339
  group: Joi.object().instance(Group),
263
340
  groups: Joi.array().items(Joi.object().instance(Group)).required(),
264
341
  abilities: Joi.array().items(
@@ -267,49 +344,19 @@ export default {
267
344
  name: 'control characters',
268
345
  invert: true,
269
346
  })
270
- .max(256)
347
+ .max(256),
271
348
  ),
272
349
  inheritAbilities: Joi.boolean(),
273
- secret: Joi.string()
274
- .trim(false)
275
- .pattern(/[\x01-\x1F\x7F]/, {
276
- name: 'control characters',
277
- invert: true,
278
- })
279
- .length(21),
280
- emailChangeDate: Joi.number(),
281
- newEmailSecret: Joi.string()
282
- .trim(false)
283
- .pattern(/[\x01-\x1F\x7F]/, {
284
- name: 'control characters',
285
- invert: true,
286
- })
287
- .length(21),
288
- newEmailAddress: Joi.string()
289
- .trim(false)
290
- .email({ minDomainSegments: 1, tlds: false }),
291
- cancelEmailSecret: Joi.string()
292
- .trim(false)
293
- .pattern(/[\x01-\x1F\x7F]/, {
294
- name: 'control characters',
295
- invert: true,
296
- })
297
- .length(21),
298
- cancelEmailAddress: Joi.string()
299
- .trim(false)
300
- .email({ minDomainSegments: 1, tlds: false }),
301
- recoverSecret: Joi.string()
302
- .trim(false)
303
- .pattern(/[\x01-\x1F\x7F]/, {
304
- name: 'control characters',
305
- invert: true,
306
- })
307
- .length(10),
308
- recoverSecretDate: Joi.number(),
309
350
  salt: Joi.string().trim(false),
310
351
  password: Joi.string().trim(false).required(),
352
+ revokeTokenDate: Joi.number(),
353
+ totpSecret: Joi.string()
354
+ .trim(false)
355
+ .min(24)
356
+ .max(40)
357
+ .pattern(/^[A-Z234567]+$/, 'base32 encoded unpadded'),
311
358
  }),
312
- 'Invalid User: '
359
+ 'Invalid User: ',
313
360
  );
314
361
  },
315
362
  } as Config;
@@ -0,0 +1,20 @@
1
+ import { Entity, EntityConstructor, Nymph } from '@nymphjs/nymph';
2
+
3
+ import Tilmeld from './Tilmeld';
4
+
5
+ export function enforceTilmeld(object: Nymph | Entity | EntityConstructor) {
6
+ let tilmeld: Tilmeld | undefined;
7
+ if (object instanceof Entity) {
8
+ tilmeld = object.$nymph.tilmeld as Tilmeld | undefined;
9
+ } else if (object instanceof Nymph) {
10
+ tilmeld = object.tilmeld as Tilmeld | undefined;
11
+ } else {
12
+ tilmeld = object.nymph.tilmeld as Tilmeld | undefined;
13
+ }
14
+
15
+ if (tilmeld == null) {
16
+ throw new Error('Tilmeld was not configured on this Nymph instance!');
17
+ }
18
+
19
+ return tilmeld;
20
+ }
@@ -1,6 +1,8 @@
1
- export class AccessControlError extends Error {
1
+ import { HttpError } from '@nymphjs/server';
2
+
3
+ export class AccessControlError extends HttpError {
2
4
  constructor(message: string) {
3
- super(message);
5
+ super(message, 403);
4
6
  this.name = 'AccessControlError';
5
7
  }
6
8
  }
@@ -1,6 +1,8 @@
1
- export class BadDataError extends Error {
1
+ import { HttpError } from '@nymphjs/server';
2
+
3
+ export class BadDataError extends HttpError {
2
4
  constructor(message: string) {
3
- super(message);
5
+ super(message, 400);
4
6
  this.name = 'BadDataError';
5
7
  }
6
8
  }
@@ -1,6 +1,8 @@
1
- export class BadEmailError extends Error {
1
+ import { HttpError } from '@nymphjs/server';
2
+
3
+ export class BadEmailError extends HttpError {
2
4
  constructor(message: string) {
3
- super(message);
5
+ super(message, 400);
4
6
  this.name = 'BadEmailError';
5
7
  }
6
8
  }
@@ -1,6 +1,8 @@
1
- export class BadUsernameError extends Error {
1
+ import { HttpError } from '@nymphjs/server';
2
+
3
+ export class BadUsernameError extends HttpError {
2
4
  constructor(message: string) {
3
- super(message);
5
+ super(message, 400);
4
6
  this.name = 'BadUsernameError';
5
7
  }
6
8
  }
package/src/index.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export * from './conf';
2
2
 
3
+ export * from './enforceTilmeld';
4
+
3
5
  export * from './errors';
4
6
 
5
7
  import AbleObject from './AbleObject';
@@ -11,36 +11,36 @@ export default {
11
11
  TilmeldAccessLevels.NO_ACCESS,
12
12
  TilmeldAccessLevels.READ_ACCESS,
13
13
  TilmeldAccessLevels.WRITE_ACCESS,
14
- TilmeldAccessLevels.FULL_ACCESS
14
+ TilmeldAccessLevels.FULL_ACCESS,
15
15
  ),
16
16
  acGroup: Joi.number().valid(
17
17
  TilmeldAccessLevels.NO_ACCESS,
18
18
  TilmeldAccessLevels.READ_ACCESS,
19
19
  TilmeldAccessLevels.WRITE_ACCESS,
20
- TilmeldAccessLevels.FULL_ACCESS
20
+ TilmeldAccessLevels.FULL_ACCESS,
21
21
  ),
22
22
  acOther: Joi.number().valid(
23
23
  TilmeldAccessLevels.NO_ACCESS,
24
24
  TilmeldAccessLevels.READ_ACCESS,
25
25
  TilmeldAccessLevels.WRITE_ACCESS,
26
- TilmeldAccessLevels.FULL_ACCESS
26
+ TilmeldAccessLevels.FULL_ACCESS,
27
27
  ),
28
28
  acRead: Joi.array().items(
29
29
  Joi.alternatives().try(
30
30
  Joi.object().instance(User),
31
- Joi.object().instance(Group)
32
- )
31
+ Joi.object().instance(Group),
32
+ ),
33
33
  ),
34
34
  acWrite: Joi.array().items(
35
35
  Joi.alternatives().try(
36
36
  Joi.object().instance(User),
37
- Joi.object().instance(Group)
38
- )
37
+ Joi.object().instance(Group),
38
+ ),
39
39
  ),
40
40
  acFull: Joi.array().items(
41
41
  Joi.alternatives().try(
42
42
  Joi.object().instance(User),
43
- Joi.object().instance(Group)
44
- )
43
+ Joi.object().instance(Group),
44
+ ),
45
45
  ),
46
46
  };
package/tsconfig.json CHANGED
@@ -2,10 +2,10 @@
2
2
  "extends": "@tsconfig/recommended/tsconfig.json",
3
3
 
4
4
  "compilerOptions": {
5
- "lib": ["ES2021"],
6
- "target": "ES2021",
5
+ "lib": ["ES2023"],
6
+ "target": "ES2022",
7
7
  "noImplicitAny": true,
8
- "removeComments": true,
8
+ "removeComments": false,
9
9
  "sourceMap": true,
10
10
  "outDir": "dist",
11
11
  "resolveJsonModule": true,
package/typedoc.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": ["../../typedoc.base.json"],
3
+ "entryPoints": ["src/index.ts"]
4
+ }