@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
@@ -1,233 +0,0 @@
1
- import { attributes, relationAttributes } from './attrs.js'
2
- import { childTypes, objectTypes } from './object-structure.ts'
3
-
4
- const normalize = str =>
5
- str
6
- .toLowerCase()
7
- .replaceAll(/[^a-z0-9]+/g, ' ')
8
- .trim()
9
- .replaceAll(' ', '-')
10
-
11
- const assertDef = def => {
12
- const {
13
- type,
14
- name,
15
- attrs,
16
- children: _children,
17
- childrenAttrs,
18
- referencePath: _referencePath,
19
- ...rest
20
- } = def
21
- const [extra] = Object.keys(rest)
22
- if (extra) {
23
- throw Error(
24
- `Unexpected property ${extra}, did you mean to define an attribute ?`,
25
- )
26
- }
27
- if (type === 'campus') throw Error(`Campuses can't be defined in definitions`)
28
- if (!objectTypes.has(type)) throw Error(`Invalid type property`)
29
- if (!name || typeof name !== 'string') throw Error(`Invalid name property`)
30
- if (!attrs || typeof attrs !== 'object' || Array.isArray(attrs)) {
31
- throw Error(`Invalid attrs property`)
32
- }
33
- if (childrenAttrs) throw Error(`childrenAttrs is no longer supported`)
34
-
35
- for (const key of Object.keys(attrs || {})) {
36
- if (relationAttributes[key]) {
37
- throw Error(
38
- `Attr ${key} should be defined in the relation with its parent, not on the object itself.`,
39
- )
40
- }
41
-
42
- const matches = attributes[key]
43
- if (!matches) throw Error(`Undefined attr ${key}`)
44
- const attrDefs = matches[type]
45
- if (!attrDefs) {
46
- const types = Object.keys(matches).join(', ')
47
- throw Error(`${type} does not match any of ${types} for attr ${key}`)
48
- }
49
-
50
- if (attrDefs.deprecated) {
51
- throw Error(`attr ${key} deprecated: ${attrDefs.deprecated}`)
52
- }
53
- }
54
- }
55
-
56
- const buildTree = (
57
- { name, type, attrs, children, referencePath },
58
- parent,
59
- depth,
60
- ) => {
61
- // As we check for the parent / child type structure, this should never happen
62
- // so we did not bother to make a more descriptive error for this case
63
- if (depth > 100) throw Error('Unexpected very deep tree, maybe circular ?')
64
- const object = { name, type, attrs, children: {}, referencePath, parent }
65
- if (!children) return object
66
- let prev
67
- for (const [key, { ref, ...rest }] of Object.entries(children)) {
68
- const child = buildTree(ref, object, (depth || 0) + 1)
69
- child.attrs = { ...child.attrs, ...rest }
70
- prev && (prev.next = child)
71
- child.prev = prev
72
- object.children[key] = child
73
- }
74
- return object
75
- }
76
-
77
- const assertRelation = (parent, key, relation) => {
78
- if (!/^[a-z0-9-]*$/.test(key)) {
79
- throw Error(
80
- `Invalid key for child ${key} Kebab-case key suggestion: ${normalize(
81
- key,
82
- )}`,
83
- )
84
- }
85
-
86
- const child = relation.ref
87
- const { type } = child
88
- const allowedTypes = childTypes[parent.type]
89
- if (!allowedTypes.includes(type)) {
90
- throw Error(`Type ${type} ${key} is not a possible child`)
91
- }
92
-
93
- // Should never happen
94
- if (parent.referencePath === child.referencePath) {
95
- throw Error(`Self reference in child ${key}`)
96
- }
97
-
98
- for (const key of Object.keys(relation)) {
99
- if (key === 'ref') continue
100
- const matches = attributes[key]
101
- if (matches?.[type]?.restrictive) {
102
- throw Error(
103
- `Attr ${key} should be defined on the object itself, not in the relation with its parent.`,
104
- )
105
- }
106
- const relMatches = relationAttributes[key]
107
- if (!matches && !relMatches) throw Error(`Undefined attr ${key}`)
108
- const attrDefs = (relMatches?.[parent.type] || matches)[type]
109
- if (!attrDefs) {
110
- const types = Object.keys(matches).join(', ')
111
- throw Error(`${type} does not match any of ${types} for attr ${key}`)
112
- }
113
-
114
- if (attrDefs.deprecated) {
115
- throw Error(`attr ${key} deprecated: ${attrDefs.deprecated}`)
116
- }
117
- }
118
- }
119
-
120
- const checkAttrs = object => {
121
- for (const [key, value] of Object.entries(object.attrs)) {
122
- try {
123
- const attrDefs =
124
- relationAttributes[key]?.[object.parent?.type]?.[object.type] ||
125
- attributes[key][object.type]
126
-
127
- if (value?.type === 'function') {
128
- const fn = attrDefs.functionsByName?.[value.name]
129
- if (!fn) throw Error(`function ${value.name} not found for attr ${key}`)
130
- } else if (!attrDefs.check(value, object)) {
131
- throw Error(`wrong type for attr ${key}`)
132
- }
133
- } catch (err) {
134
- err.parentRef = object.parent?.referencePath
135
- err.childRef = object.referencePath
136
- err.value = value
137
- err.key = key
138
- throw err
139
- }
140
- }
141
-
142
- for (const child of Object.values(object.children)) {
143
- checkAttrs(child)
144
- }
145
- }
146
-
147
- const isExam = def => def.type === 'exam'
148
- const assertExams = definitions => {
149
- const groups = {}
150
- for (const exam of definitions.filter(isExam)) {
151
- let prevExamGroup = 0
152
- for (const [key, exercise] of Object.entries(exam.children)) {
153
- try {
154
- const { group } = exercise
155
- if (!group) throw Error('missing exam group')
156
-
157
- const groupDiff = group - prevExamGroup
158
- prevExamGroup = group
159
- if (groupDiff > 1) throw Error('exam must not have gaps in groups')
160
- if (groupDiff < 0) {
161
- throw Error('exercise must be sorted by group from lower to greater')
162
- }
163
-
164
- // exam exercise group should stay consistent across all exams
165
- const prevGroup = groups[key]
166
- if (prevGroup == null) {
167
- groups[key] = group
168
- } else if (prevGroup !== group) {
169
- throw Error('Exercise used in different exam groups')
170
- }
171
- } catch (err) {
172
- err.exam = exam.referencePath
173
- err.exercise = exercise.ref.referencePath
174
- throw err
175
- }
176
- }
177
- }
178
- }
179
-
180
- export const checkAndBuildDefinitions = async readDef => {
181
- const cache = {}
182
- const getDefs = async ([key, relation]) => {
183
- let def
184
- try {
185
- const cached = cache[key]
186
- def = await (cached || (cache[key] = readDef(key)))
187
- relation && (relation.ref = def)
188
- if (cached) return [] // skip assert and children checks if already cached
189
- } catch (err) {
190
- err.name = key
191
- throw err
192
- }
193
- try {
194
- assertDef(def)
195
- } catch (err) {
196
- err.type = def.type
197
- err.referencePath = def.referencePath
198
- throw err
199
- }
200
- const relations = Object.entries(def.children || {}).map(getDefs)
201
- try {
202
- return [def, ...(await Promise.all(relations)).flat()]
203
- } catch (err) {
204
- // Some content may be used at multiple place (ex: exam exercises)
205
- // so we may have multiple parents
206
- key && (err.parents || (err.parents = [])).push(key)
207
- throw err
208
- }
209
- }
210
-
211
- const definitions = await getDefs([])
212
-
213
- // Assert all relations looks ok, possible child / parent, allowed attributes
214
- for (const def of definitions) {
215
- if (!def.children) continue
216
- try {
217
- for (const entry of Object.entries(def.children)) {
218
- assertRelation(def, entry[0], entry[1])
219
- }
220
- } catch (err) {
221
- err.type = def.type
222
- err.referencePath = def.referencePath
223
- throw err
224
- }
225
- }
226
-
227
- // Check all attributes in context
228
- const root = buildTree(definitions[0])
229
- checkAttrs(root)
230
- assertExams(definitions)
231
-
232
- return { definitions, root }
233
- }
package/event-utils.js DELETED
@@ -1,58 +0,0 @@
1
- export const SEC = 1000
2
- export const MIN = 60 * SEC
3
- export const HOUR = 60 * MIN
4
- export const DAY = 24 * HOUR
5
- export const WEEK = 7 * DAY
6
-
7
- export const numTime = date => new Date(date).getTime()
8
- export const numToISOString = number =>
9
- number ? new Date(number).toISOString() : Number.NaN
10
- export const getChildRegistrationStartAt = (parentEventStartAt, child) => {
11
- const {
12
- startAfter = 0,
13
- eventStartDelay = 0,
14
- registrationDuration,
15
- } = child.attrs
16
- return (
17
- numTime(parentEventStartAt) +
18
- (startAfter - (eventStartDelay + registrationDuration)) * MIN
19
- )
20
- }
21
- export const getChildRegistrationEndAt = (parentEventStartAt, child) => {
22
- const { startAfter = 0, eventStartDelay = 0 } = child.attrs
23
- return numTime(parentEventStartAt) + (startAfter - eventStartDelay) * MIN
24
- }
25
- export const getChildEventStartAt = (parentEventStartAt, { attrs }) =>
26
- numTime(parentEventStartAt) + (attrs.startAfter || 0) * MIN
27
-
28
- export const getChildEventEndAt = (parentEventStartAt, child) => {
29
- const { startAfter = 0, eventDuration } = child.attrs
30
- return numTime(parentEventStartAt) + (startAfter + eventDuration) * MIN
31
- }
32
-
33
- export const getChildEventInfo = (parentEventStartAt, child) => ({
34
- startAt: numToISOString(
35
- getChildRegistrationStartAt(parentEventStartAt, child),
36
- ),
37
- endAt: numToISOString(getChildRegistrationEndAt(parentEventStartAt, child)),
38
- eventJoinedAt: numToISOString(
39
- getChildEventStartAt(parentEventStartAt, child),
40
- ),
41
- eventEndAt: numToISOString(getChildEventEndAt(parentEventStartAt, child)),
42
- })
43
-
44
- // NB: useful when creating event for first time (add event)
45
- export const getEndOfEvent = ({ eventJoinedAt }, object) =>
46
- numTime(eventJoinedAt) + object.attrs.eventDuration * MIN
47
-
48
- // TODO: rename this fn - also used for project
49
- // the best would be to find a word to label the child items that are not events
50
- export const getQuestStartAt = (parentEventStartAt, quest) =>
51
- numTime(parentEventStartAt) + quest.attrs.delay
52
- export const getQuestExtraEndAt = (questStartAt, quest) => {
53
- const { scopeExtraDuration, duration } = quest.attrs
54
- return questStartAt + ((scopeExtraDuration || 0) + (duration || 1)) * DAY
55
- }
56
-
57
- // TODO: rename isPassed
58
- export const isFinished = date => new Date(date).getTime() < Date.now()
package/graph.js DELETED
@@ -1,96 +0,0 @@
1
- const isCoreObj = (contentShape, coreKey) =>
2
- typeof contentShape === 'object' && Object.keys(contentShape)[0] === coreKey
3
- const isSatteliteObj = (contentShape, satteliteKey) =>
4
- typeof contentShape === 'object' &&
5
- Object.values(contentShape)[0].some(sat => sat === satteliteKey)
6
-
7
- export const getCoreSattelites = (coreObject, objectsList) => {
8
- const { key, parent } = coreObject
9
- const objects = objectsList || Object.values(parent.children)
10
- const sattelitesSection = parent.attrs.graph.innerCircle.find(
11
- s => s.type === 'slice' && s.innerArc.contents.find(c => isCoreObj(c, key)),
12
- )
13
- if (!sattelitesSection) return []
14
- const sattelitesObject = sattelitesSection.innerArc.contents.find(c =>
15
- isCoreObj(c, key),
16
- )
17
- const sattelitesList = new Set(...Object.values(sattelitesObject))
18
-
19
- return objects.filter(o => sattelitesList.has(o.key))
20
- }
21
-
22
- export const getCoreOfSattelite = (satteliteObject, objectsList) => {
23
- const { key, parent } = satteliteObject
24
- const objects = objectsList || Object.values(parent.children)
25
- const sattelitesSection = parent.attrs.graph?.innerCircle?.find(
26
- s =>
27
- s.type === 'slice' &&
28
- s.innerArc?.contents.find(c => isSatteliteObj(c, key)),
29
- )
30
- if (!sattelitesSection) return undefined
31
- const coreObject = sattelitesSection.innerArc.contents.find(c =>
32
- isSatteliteObj(c, key),
33
- )
34
-
35
- return objects.filter(o => o.key === Object.keys(coreObject)[0])
36
- }
37
-
38
- export const flatGraphContents = graph => [
39
- ...(graph.centralPoint ? [graph.centralPoint] : []),
40
- ...(graph.innerCircle || []).flatMap(section =>
41
- section.type === 'slice'
42
- ? [
43
- ...(section.entryPoint ? [section.entryPoint] : []),
44
- ...(section.innerArc?.contents || []).flatMap(c =>
45
- typeof c === 'string' ? c : Object.entries(c)[0].flat(),
46
- ),
47
- ...(section.outerArcs || []).flatMap(arc => arc.contents || []),
48
- ]
49
- : section.contents,
50
- ),
51
- ...(graph.middleCircle || []).flatMap(arc => arc.contents || []),
52
- ...(graph.outerCircle || []).flatMap(arc => arc.contents || []),
53
- ]
54
-
55
- // WARNING: without central point (for linear graph, already destructured)
56
- export const flatAtomeAndCoreContents = graph => [
57
- ...(graph.innerCircle || []).flatMap(section =>
58
- section.type === 'slice'
59
- ? [
60
- ...(section.entryPoint ? [section.entryPoint] : []),
61
- ...(section.innerArc?.contents || []).flatMap(c =>
62
- typeof c === 'string' ? c : Object.keys(c)[0],
63
- ),
64
- ...(section.outerArcs || []).flatMap(arc => arc.contents || []),
65
- ]
66
- : section.contents,
67
- ),
68
- ...(graph.middleCircle || []).flatMap(arc => arc.contents || []),
69
- ...(graph.outerCircle || []).flatMap(arc => arc.contents || []),
70
- ]
71
-
72
- export const limitations = {
73
- LINE: {
74
- maxLinesCount: 6,
75
- maxContentsCount: 7, // 7 contents max split along a single line
76
- },
77
- SLICE: {
78
- maxSlicesCount: 4,
79
- innerCircle: {
80
- maxSubContentsCount: 5, // 5 sub-contents max by content
81
- maxContentsCount: 30, // 30 contents max spread evenly over the number of inner cirlces of slices
82
- },
83
- outerArc: {
84
- maxArcsCount: 2,
85
- maxContentsCount: 10, // 10 contents max spread evenly over the 1 or 2 outer arcs
86
- },
87
- },
88
- MIDDLE_CIRCLE: {
89
- maxArcsCount: 8,
90
- maxContentsCount: 70, // 70 contents max spread evenly over the number of arcs
91
- },
92
- OUTER_CIRCLE: {
93
- maxArcsCount: 9,
94
- maxContentsCount: 90, // 90 contents max spread evenly over the number of arcs
95
- },
96
- }
package/hasura-core.js DELETED
@@ -1,217 +0,0 @@
1
- export class HasuraError extends Error {
2
- constructor({ extensions, message, ...props }, cause) {
3
- super(message, cause)
4
- Object.assign(this, props)
5
- Object.assign(this, extensions)
6
- Error.captureStackTrace?.(this, HasuraError)
7
- }
8
- }
9
-
10
- export const initClient = ({ debug, address, log, ...params }) => {
11
- log || (log = debug ? console.debug : () => {})
12
- const handlers = new Map()
13
- const subscribers = new Map()
14
-
15
- const getId = () => {
16
- const id = Math.random().toString(36).slice(2)
17
- return handlers.has(id) ? getId() : id
18
- }
19
-
20
- const rejectAllPending = err => {
21
- subscribers.clear() // TODO: store subscribers query and re-trigger them
22
- for (const [id, { reject, noCleanup }] of handlers) {
23
- noCleanup || activeQueries.delete(id)
24
- handlers.delete(id)
25
- reject(err)
26
- }
27
- return err
28
- }
29
-
30
- const end = (handler, props = {}) => {
31
- props.duration = Date.now() - handler.start
32
- props.size = handler.size
33
- props.name = handler.query
34
- props.id = handler.id
35
- log('query', props)
36
- handlers.delete(handler.id)
37
- handler.noCleanup || activeQueries.delete(handler.id)
38
- }
39
-
40
- const messageFail = (handler, payload, id) => {
41
- if (!handler) return log('missing-handler', { id, type: 'error' })
42
-
43
- end(handler, { payload, type: 'error' })
44
- handlers.delete(id)
45
- return handler.reject(
46
- new HasuraError(payload.errors[0], debug && { cause: handler.cause }),
47
- )
48
- }
49
-
50
- const handleMessage = (data, resolve, reject) => {
51
- if (data === '{"type":"ka"}') return // ignore keep alive
52
-
53
- const { type, payload, id } = JSON.parse(data)
54
- const handler = handlers.get(id)
55
- handler && (handler.size += data.length)
56
-
57
- log('raw', data)
58
-
59
- switch (type) {
60
- case 'connection_ack': {
61
- return resolve(payload)
62
- }
63
-
64
- case 'connection_error': {
65
- const err = rejectAllPending(new HasuraError({ errors: [payload] }))
66
- return reject(err)
67
- }
68
-
69
- case 'data': {
70
- if (payload.errors) return messageFail(handler, payload, id)
71
-
72
- const sub = subscribers.get(id)
73
- if (!sub) {
74
- return handler
75
- ? (handler.payload = payload)
76
- : log('missing-handler', { id, type: 'error' })
77
- }
78
-
79
- sub(payload.data)
80
- if (handler) {
81
- end(handler, { type, payload })
82
- handler.resolve()
83
- }
84
-
85
- return
86
- }
87
-
88
- case 'error': {
89
- return messageFail(handler, payload, id)
90
- }
91
-
92
- case 'complete': {
93
- if (!handler) return
94
- end(handler, { type, payload })
95
- return handler.resolve(handler.payload?.data)
96
- }
97
- }
98
- }
99
-
100
- const handleFail = (event, type) =>
101
- rejectAllPending(
102
- new HasuraError({ message: `WebSocket connection ${type}`, event }),
103
- )
104
-
105
- let ws = new WebSocket(address, 'graphql-ws')
106
- let activeQueries = new Map()
107
- const exec = async (id, payload, name, noCleanup) => {
108
- await connection
109
- const handler = {
110
- id,
111
- size: 0,
112
- query: name,
113
- start: Date.now(),
114
- noCleanup,
115
- }
116
- const result = new Promise((resolve, reject) => {
117
- handler.resolve = resolve
118
- handler.reject = reject
119
- })
120
- debug && (handler.cause = Error('hasuraClient.exec'))
121
- handlers.set(id, handler)
122
- activeQueries.set(id, { payload, name })
123
- log('start', { id, payload })
124
- ws.send(`{"type":"start","id":"${id}","payload":${payload}}`)
125
- return result
126
- }
127
- const runFromString = (payload, name) => exec(getId(), payload, name)
128
-
129
- const subscribeFromString = (sub, payload, name) => {
130
- const id = getId()
131
- subscribers.set(id, sub)
132
-
133
- return {
134
- execution: exec(id, payload, name, true),
135
- unsubscribe: () => {
136
- subscribers.delete(id)
137
- activeQueries.delete(id)
138
- log('stop', { id })
139
- ws.send(`{"type":"stop","id":"${id}"}`)
140
- },
141
- }
142
- }
143
-
144
- let connected = false
145
- const getConnection = async () => {
146
- connected = false
147
- const { promise, resolve, reject } = Promise.withResolvers()
148
- const onError = () => reject(handleFail(undefined, 'failed'))
149
- const onClose = event =>
150
- reject(handleFail({ code: event.code, reason: event.reason }, 'close'))
151
- ws.addEventListener('error', onError, { once: true })
152
- ws.addEventListener('close', onClose, { once: true })
153
- ws.addEventListener('message', event =>
154
- handleMessage(event.data, resolve, reject),
155
- )
156
- try {
157
- await promise
158
- return (connected = true)
159
- } finally {
160
- ws.removeEventListener('error', onError)
161
- ws.removeEventListener('close', onClose)
162
- }
163
- }
164
-
165
- let connection = getConnection()
166
-
167
- const connect = async ({ adminSecret, token, role, headers }) => {
168
- const previousActiveQueries = activeQueries
169
- const reload = connected
170
- if (reload) {
171
- ws.close()
172
- ws = new WebSocket(address, 'graphql-ws')
173
- connection = getConnection()
174
- activeQueries = new Map()
175
- }
176
-
177
- if (!ws.readyState) {
178
- await new Promise(s => ws.addEventListener('open', s, { once: true }))
179
- }
180
-
181
- const payload = {
182
- headers: adminSecret
183
- ? { 'x-hasura-admin-secret': adminSecret, ...headers }
184
- : { Authorization: `Bearer ${token}`, ...headers },
185
- }
186
-
187
- role && (payload.headers['x-hasura-role'] = role)
188
-
189
- ws.send(JSON.stringify({ type: 'connection_init', payload }))
190
-
191
- reload &&
192
- connection.then(() => {
193
- // re exec all previous active queries
194
- for (const [id, { payload, name }] of previousActiveQueries) {
195
- exec(id, payload, name)
196
- }
197
- })
198
-
199
- return connection
200
- }
201
-
202
- if (params.adminSecret || params.token) {
203
- connect(params)
204
- }
205
-
206
- return {
207
- ws,
208
- connect,
209
- connection,
210
- runFromString,
211
- subscribeFromString,
212
- run: (query, variables) =>
213
- runFromString(JSON.stringify({ query, variables })),
214
- subscribe: (sub, query, variables) =>
215
- subscribeFromString(sub, JSON.stringify({ query, variables })),
216
- }
217
- }