@01-edu/shared 2.0.5 → 2.0.7

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 (46) hide show
  1. package/dist/attrs-defs.js +1 -0
  2. package/dist/attrs.js +1 -0
  3. package/dist/bin/check-definitions.js +3 -0
  4. package/dist/chunk-727IHWVW.js +1 -0
  5. package/dist/chunk-7ZTI6THC.js +1 -0
  6. package/dist/chunk-CBWHFDVB.js +1 -0
  7. package/dist/chunk-EI7MMDWY.js +1 -0
  8. package/dist/chunk-EOY6G4KE.js +1 -0
  9. package/dist/chunk-K5Z4W5GV.js +1 -0
  10. package/dist/chunk-LDUPRVV4.js +1 -0
  11. package/dist/chunk-NQCTSDJE.js +1 -0
  12. package/dist/chunk-V47RDOUO.js +1 -0
  13. package/dist/chunk-XYW3ROIR.js +1 -0
  14. package/dist/definitions-checker.js +1 -0
  15. package/dist/event-utils.js +1 -0
  16. package/dist/games-utils.js +1 -0
  17. package/dist/graph.js +1 -0
  18. package/dist/hasura-core.js +1 -0
  19. package/dist/hasura-model.js +34 -0
  20. package/dist/hasura-prepare.js +1 -0
  21. package/dist/modular-steps-utils.js +1 -0
  22. package/dist/object-structure.js +1 -0
  23. package/dist/onboarding.js +1 -0
  24. package/dist/path.js +1 -0
  25. package/dist/qa-utils.js +1 -0
  26. package/dist/score.js +1 -0
  27. package/dist/skill-definitions.js +1 -0
  28. package/dist/toolbox.js +1 -0
  29. package/package.json +17 -6
  30. package/attrs-defs.js +0 -4273
  31. package/attrs.js +0 -423
  32. package/bin/check-definitions.js +0 -74
  33. package/definitions-checker.js +0 -233
  34. package/event-utils.js +0 -58
  35. package/graph.js +0 -96
  36. package/hasura-core.js +0 -217
  37. package/hasura-model.js +0 -138
  38. package/hasura-prepare.js +0 -44
  39. package/languages.js +0 -147
  40. package/onboarding.js +0 -25
  41. package/path.js +0 -73
  42. package/programming-languages.js +0 -21
  43. package/qa-utils.js +0 -13
  44. package/score.js +0 -80
  45. package/skill-definitions.js +0 -359
  46. package/toolbox.js +0 -532
package/attrs.js DELETED
@@ -1,423 +0,0 @@
1
- import { attrs, relationAttrs } from './attrs-defs.js'
2
- import { languages } from './languages.js'
3
- import { mapEntries, mapValues } from './toolbox.js'
4
-
5
- // ⚡ description placeholder
6
-
7
- const typeCheckers = {
8
- boolean: b => typeof b === 'boolean',
9
- number: n => typeof n === 'number',
10
- string: s => typeof s === 'string',
11
- object: o => typeof o === 'object' && o !== null,
12
- array: Array.isArray,
13
- }
14
-
15
- const determinType = value => {
16
- if (Array.isArray(value)) return 'array'
17
- if (typeof value === 'object' && value !== null) return value.type || 'object'
18
- return typeof value
19
- }
20
- const typeChecker = (defs, value, object, key) => {
21
- const { type, check, options } = defs
22
-
23
- if (value == null) {
24
- if (!defs.required || defs.value !== undefined) return true
25
- // if no value for required attribute without a default value, reject
26
- throw Error(`missing value for required attribute ${key}`)
27
- }
28
-
29
- if (!type) throw Error(`attribute type definition is missing for ${key}`)
30
-
31
- if (value?.type === 'function') {
32
- if (defs.functionsByName[value.name]) return true
33
- throw Error(`function associated not allowed for ${key}`)
34
- }
35
-
36
- // if a check is defined and don't pass, reject
37
- check?.(value, object, key)
38
-
39
- if (options) {
40
- const opts =
41
- typeof options === 'function' ? options(object, value) : options
42
- if (opts.length === 1) {
43
- if (opts[0] !== value) {
44
- throw Error(`${key} must be ${opts[0]} but was ${value}`)
45
- }
46
- } else {
47
- const isAnOption = opts.includes(value)
48
- if (!isAnOption) {
49
- throw Error(
50
- `invalid option for ${key}: should be included in ${opts.join(', ')}`,
51
- )
52
- }
53
- }
54
- }
55
-
56
- // basic case
57
- if (typeCheckers[type]) {
58
- if (!typeCheckers[type](value)) throw Error(`Expect ${type} for ${key}`)
59
- return true
60
- }
61
-
62
- // complex cases: array
63
- if (Array.isArray(type)) {
64
- if (!Array.isArray(value)) {
65
- throw Error(`invalid attribute value: expects an array for ${key}`)
66
- }
67
- // every value have to match one of the type definition
68
-
69
- const uniqueDef = type.length === 1 && type[0]
70
- // convert array type into object for better accessibility
71
- const types =
72
- !uniqueDef &&
73
- Object.fromEntries(
74
- type.map(t => [
75
- Array.isArray(t.type) ? 'array' : t.type?.type?.value || t.type,
76
- t,
77
- ]),
78
- )
79
-
80
- for (const [index, v] of value.entries()) {
81
- const err = Error('checks failed for all types')
82
- err.index = index
83
- err.key = key
84
- err.label = defs.label
85
- const subdefs = uniqueDef || types[determinType(v)]
86
-
87
- if (!subdefs) {
88
- err.details = {
89
- label: 'Unknown structure',
90
- err: Error('no type matches the value'),
91
- }
92
- throw err
93
- }
94
- try {
95
- typeChecker(subdefs, v, object, key)
96
- } catch (error) {
97
- err.details = error.details || {
98
- label: subdefs.label || error.label,
99
- err: error,
100
- }
101
- throw err
102
- }
103
- }
104
-
105
- // TODO: check "one of" also one day - meaning there is no duplicate
106
-
107
- return true
108
- }
109
-
110
- if (typeof type !== 'object') throw Error('invalid attribute type definition')
111
-
112
- if (typeof value !== 'object' || value === null) {
113
- throw Error('invalid attribute value: expects an object')
114
- }
115
-
116
- // then is necessarily an object. Let's check!
117
- const typeEntries = Object.entries(type)
118
-
119
- // check that every key fulfilled is defined in the type definitions
120
- const invalidKey = Object.keys(value).find(key => !type[key])
121
- if (invalidKey) throw Error(`${invalidKey} is invalid.`)
122
-
123
- for (const [key, defs] of typeEntries) {
124
- typeChecker(defs, value[key], object, key)
125
- }
126
- return true
127
- }
128
-
129
- // same as attrs with the check & function by name generated and descriptions form markdown files
130
- export const attributes = mapEntries(attrs, ([attrKey, matches]) => [
131
- attrKey,
132
- mapValues(matches, (defs /* type */) => ({
133
- ...defs,
134
- check: (value, object) => typeChecker(defs, value, object, attrKey),
135
- })),
136
- ])
137
-
138
- export const relationAttributes = mapEntries(
139
- relationAttrs,
140
- ([attrKey, byParent]) => [
141
- attrKey,
142
- mapValues(byParent, (matches /* parentType */) =>
143
- mapValues(matches, (defs /* childType */) => ({
144
- ...defs,
145
- check: (value, object) => typeChecker(defs, value, object, attrKey),
146
- })),
147
- ),
148
- ],
149
- )
150
-
151
- // white list of attributes that can be applied in bulk
152
- const allowedBulkAttrs = {
153
- codeEditor: { enabled: {} },
154
- validations: {
155
- // this is for the different options of validation,
156
- // we know that raids for now is the only one that contains multiple validations
157
- // and we **don't** want admin_selection
158
- type: [
159
- 'admin_audit',
160
- 'tester',
161
- 'dedicated_auditors_for_event',
162
- 'user_audit',
163
- ],
164
- ratio: {},
165
- required: {},
166
- matchInfluence: {},
167
- cooldown: {},
168
- preQuestions: {},
169
- postQuestions: {},
170
- matchWhere: {},
171
- },
172
- }
173
- export const getAllBulkAttrs = (parentType, childType) => {
174
- const relationAttrs = getDefaultRelAttrs(parentType, childType)
175
- const attrs = getDefaultAttrs({ type: childType })
176
- const allAttrs = { ...relationAttrs, ...attrs }
177
- return filterBulkAttrs(allAttrs, allowedBulkAttrs)
178
- }
179
- export const filterBulkAttrs = (attrs, allowedAttrs) => {
180
- if (!attrs) return {}
181
- if (Array.isArray(attrs.type)) {
182
- const { type, ...rest } = attrs
183
- // if it is type array and from the validation attribute we need to filter the validation type
184
- const types = type
185
- .map(a => filterBulkAttrs(a, allowedAttrs))
186
- .filter(({ type }) => allowedAttrs.type.includes(type.type.value))
187
- return { ...rest, type: types }
188
- }
189
- if (attrs.type && typeof attrs.type === 'object') {
190
- const { type, ...rest } = attrs
191
- const filtered = { type: {}, ...rest }
192
- const typesKeys = Object.keys(type)
193
- for (const t of typesKeys) {
194
- if (allowedAttrs?.[t]) {
195
- filtered.type[t] = type[t]
196
- }
197
- }
198
- return filtered
199
- }
200
- if (typeof attrs === 'object' && !attrs.type) {
201
- const filtered = {}
202
- const keys = Object.keys(attrs)
203
- for (const k of keys) {
204
- if (allowedAttrs[k]) {
205
- filtered[k] = filterBulkAttrs(attrs[k], allowedAttrs[k])
206
- }
207
- }
208
- return filtered
209
- }
210
- return null
211
- }
212
-
213
- // map from attrs[name][type] to attrs[type][name]
214
- export const attrsByType = {}
215
-
216
- const languagesEntries = Object.entries(languages)
217
- for (const [name, matches] of Object.entries(attributes)) {
218
- for (const [type, defs] of Object.entries(matches)) {
219
- const ofType = attrsByType[type] || (attrsByType[type] = {})
220
- ofType[name] = defs
221
- // handle translations: generate translation attrs and required status
222
- const { label, ...restDefs } = defs
223
- if (defs.functionsByName?.translate) {
224
- // perf measures done: increase from 0.671ms loadtime to 13.261ms
225
- // should not impact the perfs
226
- for (const [code, language] of languagesEntries) {
227
- const newLabel = `${label} - ${language}`
228
- ofType[`${name}-${code}`] = {
229
- ...restDefs,
230
- label: newLabel,
231
- lgCode: code,
232
- required: false,
233
- }
234
- }
235
- }
236
- }
237
- }
238
-
239
- // map from attrs[name][parentType][childType]
240
- // to attrs[parentType][childType][name]
241
- export const relationAttrsByParentType = {}
242
- for (const [name, byParent] of Object.entries(relationAttributes)) {
243
- for (const [parentType, byChild] of Object.entries(byParent)) {
244
- for (const [childType, defs] of Object.entries(byChild)) {
245
- const ofParentType =
246
- relationAttrsByParentType[parentType] ||
247
- (relationAttrsByParentType[parentType] = {})
248
- const ofParentChildType =
249
- ofParentType[childType] || (ofParentType[childType] = {})
250
- ofParentChildType[name] = defs
251
- // for now, there is no relation attr that is translatable
252
- // if it is created one day, add handling of translations here
253
- }
254
- }
255
- }
256
- const attrsEntriesByType = mapValues(attrsByType, Object.entries)
257
- const relAttrsEntriesByParentType = mapValues(
258
- relationAttrsByParentType,
259
- byParent => mapValues(byParent, Object.entries),
260
- )
261
- export const getDefaultAttrsEntries = object =>
262
- attrsEntriesByType[object.type] || []
263
- export const getDefaultRelAttrsEntries = (parentType, childType) =>
264
- relAttrsEntriesByParentType[parentType]?.[childType] || []
265
-
266
- export const getDefaultAttrs = object => attrsByType[object.type]
267
- export const getDefaultRelAttrs = (parentType, childType) =>
268
- relationAttrsByParentType[parentType]?.[childType]
269
-
270
- const findDefaultOrNamedFunction = (value, defs) => {
271
- // Should be true only for required impure default value functions
272
- if (value === undefined || value === null) return defs.required && defs.value
273
- if (value.type !== 'function') return
274
- // Otherwise we only care about when the default function was overridden
275
- // by another impure function
276
-
277
- return defs.functionsByName?.[value.name]
278
- }
279
-
280
- const applyDefs = (key, value, defs, object, getUser) => {
281
- // xp, child.attrs.rewards, defs.type.xp, object
282
- // reward, child.attrs, defs, object
283
- if (value === undefined || value === null) return
284
- const fn = findDefaultOrNamedFunction(value[key], defs)
285
- if (typeof fn === 'function') {
286
- if (getUser) {
287
- Object.defineProperty(value, key, {
288
- get: () => fn(object, getUser(), key),
289
- enumerable: true,
290
- })
291
- } else if (fn.name === 'translate') {
292
- // if we don't have a user, assume english language
293
- value[key] = value[`${key}-en`]
294
- }
295
- }
296
- }
297
-
298
- const expandAttr = (key, value, defs, object, getUser) => {
299
- const isFunction = value[key]?.type === 'function'
300
- if (Array.isArray(defs.type) && value[key] && !isFunction) {
301
- // handle multiple sub-types defs for array items
302
- // there can be different subtypes defs of type object,
303
- // as long as they all have a type property (mandatory - to distinguish def to check)
304
- // and one subtype def for each primary data type
305
- const types =
306
- defs.type.length > 1 &&
307
- Object.fromEntries(defs.type.map(t => [t.type.type?.value || t.type, t]))
308
-
309
- for (let i = 0; i < value[key].length; i++) {
310
- let def
311
- if (types) {
312
- const isObject = typeof value[key][i] === 'object'
313
-
314
- // when type is set to "object" for items that cannot be defined in attrs because
315
- // the key cannot be known (like types.graphArcContentWithSubContents)
316
- const typeCouldNotBeDefined = isObject && Boolean(types.object)
317
-
318
- const invalidType =
319
- !value[key][i].type || typeof value[key][i].type !== 'string'
320
- const allowedTypes = Object.keys(types).join(',').slice(0, -1)
321
-
322
- if (isObject && invalidType && !typeCouldNotBeDefined) {
323
- console.warn(
324
- `Type not allowed. Object item for ${key} array (#${i} item) must have one of these "type" property: ${allowedTypes}.`,
325
- )
326
- }
327
-
328
- def = isObject ? types[value[key][i].type] : types[typeof value[key][i]]
329
-
330
- if (!def && !typeCouldNotBeDefined) {
331
- console.warn(
332
- `Missing type definition. Object item for ${key} array (#${i} item) must have one of these "type" property: ${allowedTypes}.`,
333
- )
334
- }
335
- }
336
-
337
- expandAttr(i, value[key], def || defs.type[0], object, getUser)
338
- }
339
- } else if (typeof defs.type === 'object' && value[key] && !isFunction) {
340
- for (const [childKey, subDefs] of Object.entries(defs.type)) {
341
- expandAttr(childKey, value[key], subDefs, object, getUser)
342
- }
343
- } else {
344
- applyDefs(key, value, defs, object, getUser)
345
- }
346
- }
347
-
348
- // same as the `expandDynamicAttrs` this function would resolve dynamic attributes
349
- // the special use of this function is that we can resolve certain attributes
350
- // by providing a filter function
351
- export const partialExpandDynamicAttrs = (object, getUser, filterFn) => {
352
- if (!object.children) return (object.children = {})
353
- if (!Object.keys(object.children).length) return
354
-
355
- const defautlAttrs = getDefaultAttrsEntries(object)
356
- const filteredDefaultAttrs = defautlAttrs.filter(filterFn)
357
-
358
- processAttributes(filteredDefaultAttrs, object, getUser)
359
- let prev
360
- for (const child of Object.values(object.children)) {
361
- // we only add the parent if it does not exist
362
- if (!child.parent) child.parent = object
363
- prev && (prev.next = child) && (child.prev = prev)
364
- prev = child
365
-
366
- const childDefaultAttrs = getDefaultAttrsEntries(child)
367
- const childRelAttrs = getDefaultRelAttrsEntries(object.type, child.type)
368
- const [filteredChildAttrs, filteredRelAttrs] = [
369
- childDefaultAttrs.filter(filterFn),
370
- childRelAttrs.filter(filterFn),
371
- ]
372
-
373
- // apply missing default functions
374
- // a function can only be missing if it require the user,
375
- // the event or current time
376
- processAttributes(filteredChildAttrs, child, getUser)
377
- processAttributes(filteredRelAttrs, child, getUser)
378
- partialExpandDynamicAttrs(child, getUser, filterFn)
379
- }
380
- }
381
-
382
- // this function allows us to resolve dynamic attributes
383
- // by converting them into functions that can be re-evaluated
384
- // this enables attributes to return updated values when re-checked
385
- // without requiring additional api queries
386
- // so the result can be recalculated as needed
387
- export const expandDynamicAttrs = (object, getUser) => {
388
- if (!object.children) return (object.children = {})
389
- if (!Object.keys(object.children).length) return
390
-
391
- const defautlAttrs = getDefaultAttrsEntries(object)
392
-
393
- processAttributes(defautlAttrs, object, getUser)
394
- let prev
395
- for (const child of Object.values(object.children)) {
396
- child.parent = object
397
- prev && (prev.next = child) && (child.prev = prev)
398
- prev = child
399
-
400
- const childDefaultAttrs = getDefaultAttrsEntries(child)
401
- const childRelAttrs = getDefaultRelAttrsEntries(object.type, child.type)
402
-
403
- // apply missing default functions
404
- // a function can only be missing if it require the user,
405
- // the event or current time
406
- processAttributes(childDefaultAttrs, child, getUser)
407
- processAttributes(childRelAttrs, child, getUser)
408
- expandDynamicAttrs(child, getUser)
409
- }
410
- }
411
-
412
- const processAttributes = (attrEntries, targetObject, getUser) => {
413
- for (const [key, defs] of attrEntries) {
414
- if (targetObject.attrs[key] === null) {
415
- console.warn(
416
- `value is null for ${targetObject.name} - ${key} - ${targetObject.id}`,
417
- )
418
- }
419
- expandAttr(key, targetObject.attrs, defs, targetObject, getUser)
420
- }
421
- }
422
-
423
- export { attrs, relationAttrs }
@@ -1,74 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { readFile, stat, watch } from 'node:fs/promises'
4
-
5
- import { checkAndBuildDefinitions } from '../definitions-checker.js'
6
-
7
- const rootTypes = ['module', 'piscine', 'signup', 'onboarding']
8
- const isAudit = validation => validation.type.endsWith('_audit')
9
- const readDef = async key => {
10
- const path = key == null ? 'content/def.json' : `content/${key}/def.json`
11
- const def = JSON.parse(await readFile(path, 'utf8'))
12
- def.attrs || (def.attrs = {})
13
- def.referencePath = path
14
-
15
- if (key == null && !rootTypes.includes(def.type)) {
16
- throw Error(
17
- `Root definition must be one of ${rootTypes.join(', ')}, found: ${def.type}`,
18
- )
19
- }
20
-
21
- switch (def.type) {
22
- case 'project':
23
- // biome-ignore lint/suspicious/noFallthroughSwitchClause: We want to fallthrough
24
- case 'raid': {
25
- const audit = (def.attrs.validations || []).find(isAudit)
26
- if (!audit) {
27
- throw Error('project and raid must have an audit validation specified')
28
- }
29
- if (audit.form) {
30
- throw Error(
31
- 'audit form attribute is automatically set to be ./audit/README.md, do not specify it',
32
- )
33
- }
34
- audit.form = `content/${key}/audit/README.md`
35
- await stat(audit.form)
36
- }
37
- case 'exercise': {
38
- if (def.attrs.subject) {
39
- throw Error(
40
- 'subject attribute is automatically set to be ./README.md, do not specify it',
41
- )
42
- }
43
- def.attrs.subject = `content/${key}/README.md`
44
- await stat(def.attrs.subject)
45
- }
46
- }
47
-
48
- return def
49
- }
50
-
51
- const runChecks = async () => {
52
- try {
53
- return await checkAndBuildDefinitions(readDef)
54
- } catch ({ message, stack, ...props }) {
55
- console.error(message)
56
- console.error(props)
57
- }
58
- }
59
-
60
- if (process.argv.includes('--watch') || process.argv.includes('-w')) {
61
- await runChecks()
62
- for await (const event of watch('.', { recursive: true })) {
63
- console.clear()
64
- if (!event.filename.endsWith('def.json')) continue
65
- console.log(event.eventType, 'on', event.filename, '\n')
66
- await runChecks()
67
- }
68
- } else if (!(await runChecks())) {
69
- process.exit(1)
70
- }
71
-
72
- console.log('All checks passed, no errors')
73
- // Extra checks:
74
- // - Orphan files (defs files not referenced anywhere)