@01-edu/shared 2.0.4 → 2.0.5
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.
- package/hasura-core.js +217 -0
- package/hasura-model.js +138 -0
- package/hasura-prepare.js +44 -0
- package/package.json +9 -2
- package/qa-utils.js +13 -0
- package/object-structure.ts +0 -60
package/hasura-core.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
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
|
+
}
|
package/hasura-model.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
export const buildModel =
|
|
2
|
+
prepare =>
|
|
3
|
+
(name, key = 'id', type = 'Int') => {
|
|
4
|
+
const list = `${key}_list`
|
|
5
|
+
const insertQuery = prepare(`
|
|
6
|
+
mutation insert_${name} ($objects: [${name}_insert_input!]!){
|
|
7
|
+
insert_${name} (objects: $objects) { returning { ${key} } }
|
|
8
|
+
}`)
|
|
9
|
+
|
|
10
|
+
const updateQuery = prepare(`
|
|
11
|
+
mutation update_${name} ($${key}: ${type}!, $changes: ${name}_set_input!) {
|
|
12
|
+
update_${name} (where: {${key}: {_eq: $${key}}}, _set: $changes) { affected_rows }
|
|
13
|
+
}`)
|
|
14
|
+
|
|
15
|
+
const updateQueryAll = prepare(`
|
|
16
|
+
mutation update_${name} ($${list}: [${type}!], $changes: ${name}_set_input!) {
|
|
17
|
+
update_${name} (where: {${key}: {_in: $${list}}}, _set: $changes) { affected_rows }
|
|
18
|
+
}`)
|
|
19
|
+
|
|
20
|
+
const deleteQuery = prepare(`
|
|
21
|
+
mutation delete_${name} ($${key}: ${type}!) {
|
|
22
|
+
delete_${name} (where: {${key}: {_eq: $${key}}}) { affected_rows }
|
|
23
|
+
}`)
|
|
24
|
+
|
|
25
|
+
const deleteQueryAll = prepare(`
|
|
26
|
+
mutation delete_${name} ($${list}: [${type}!]) {
|
|
27
|
+
delete_${name} (where: {id: {_in: $${list}} }) { affected_rows }
|
|
28
|
+
}`)
|
|
29
|
+
|
|
30
|
+
const getCountQuery = prepare(`
|
|
31
|
+
query ${name}_count {
|
|
32
|
+
${name}_aggregate { aggregate { count } }
|
|
33
|
+
}`)
|
|
34
|
+
|
|
35
|
+
const getKey = _ => _[key]
|
|
36
|
+
const updateOne = ({ [key]: _, ...changes }) =>
|
|
37
|
+
updateQuery({ [key]: _, changes })
|
|
38
|
+
|
|
39
|
+
const mutations = {
|
|
40
|
+
key,
|
|
41
|
+
list,
|
|
42
|
+
insertQuery,
|
|
43
|
+
deleteQuery,
|
|
44
|
+
updateQuery,
|
|
45
|
+
updateQueryAll,
|
|
46
|
+
deleteQueryAll,
|
|
47
|
+
remove: _ =>
|
|
48
|
+
Array.isArray(_)
|
|
49
|
+
? deleteQueryAll({ [list]: _ })
|
|
50
|
+
: deleteQuery({ [key]: _ }),
|
|
51
|
+
update: (changes, _) => {
|
|
52
|
+
if (!_) return updateOne(changes)
|
|
53
|
+
return Array.isArray(_)
|
|
54
|
+
? updateQueryAll({ changes, [list]: _ })
|
|
55
|
+
: updateQuery({ changes, [key]: _ })
|
|
56
|
+
},
|
|
57
|
+
add: async _ => {
|
|
58
|
+
const isArray = Array.isArray(_)
|
|
59
|
+
const result = await insertQuery.all({ objects: isArray ? _ : [_] })
|
|
60
|
+
|
|
61
|
+
return isArray
|
|
62
|
+
? result[`insert_${name}`].returning.map(getKey)
|
|
63
|
+
: result[`insert_${name}`].returning[0][key]
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return fields => {
|
|
68
|
+
const oneById = `($${key}: ${type}!) {
|
|
69
|
+
${name} (where: {${key}: {_eq: $${key}}} limit: 1) {${key} ${fields}}
|
|
70
|
+
}`
|
|
71
|
+
|
|
72
|
+
const allById = `($${list}: [${type}!]) {
|
|
73
|
+
${name} (where: {${key}: {_in: $${list}}}) {${key} ${fields}}
|
|
74
|
+
}`
|
|
75
|
+
|
|
76
|
+
const byWhere = `($where: ${name}_bool_exp!) {
|
|
77
|
+
${name} (where: $where) {${key} ${fields}}
|
|
78
|
+
}`
|
|
79
|
+
|
|
80
|
+
const toPaginate = `(
|
|
81
|
+
$where: ${name}_bool_exp!, $orderBy: ${name}_order_by!, $limit: Int!, $offset: Int!,
|
|
82
|
+
) {
|
|
83
|
+
${name} ( order_by: [$orderBy] offset: $offset limit: $limit where: $where ) { ${fields} }
|
|
84
|
+
}`
|
|
85
|
+
|
|
86
|
+
const toPaginateWithCount = `(
|
|
87
|
+
$where: ${name}_bool_exp!, $orderBy: ${name}_order_by!, $limit: Int!, $offset: Int!,
|
|
88
|
+
) {
|
|
89
|
+
${name} ( order_by: [$orderBy] offset: $offset limit: $limit where: $where ) { ${fields} }
|
|
90
|
+
${name}_aggregate (where: $where) { aggregate { count } }
|
|
91
|
+
}`
|
|
92
|
+
|
|
93
|
+
const selectQuery = prepare(`query ${oneById}`)
|
|
94
|
+
const selectQueryAll = prepare(`query ${allById}`)
|
|
95
|
+
const selectQueryWhere = prepare(`query ${byWhere}`)
|
|
96
|
+
const selectQueryPaginated = prepare(
|
|
97
|
+
`query get_${name}_paginate ${toPaginate}`,
|
|
98
|
+
)
|
|
99
|
+
const selectQueryPaginatedWithCount = prepare(
|
|
100
|
+
`query get_${name}_with_count ${toPaginateWithCount}`,
|
|
101
|
+
)
|
|
102
|
+
const subscribeQuery = prepare(`subscription ${oneById}`)
|
|
103
|
+
const subscribeQueryAll = prepare(`subscription ${allById}`)
|
|
104
|
+
const subscribeQueryWhere = prepare(`subscription ${byWhere}`)
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
...mutations,
|
|
108
|
+
selectQuery,
|
|
109
|
+
selectQueryAll,
|
|
110
|
+
selectQueryWhere,
|
|
111
|
+
subscribeQuery,
|
|
112
|
+
subscribeQueryAll,
|
|
113
|
+
subscribeQueryWhere,
|
|
114
|
+
get: _ => {
|
|
115
|
+
if (Array.isArray(_)) return selectQueryAll({ [list]: _ })
|
|
116
|
+
return _ && typeof _ === 'object'
|
|
117
|
+
? selectQueryWhere({ where: _ })
|
|
118
|
+
: selectQuery.one(_ && { [key]: _ })
|
|
119
|
+
},
|
|
120
|
+
subscribe: (sub, _) => {
|
|
121
|
+
if (Array.isArray(_)) return subscribeQueryAll(sub, { [list]: _ })
|
|
122
|
+
return _ && typeof _ === 'object'
|
|
123
|
+
? subscribeQueryWhere(sub, { where: _ })
|
|
124
|
+
: subscribeQuery.one(sub, _ && { [key]: _ })
|
|
125
|
+
},
|
|
126
|
+
getCount: async elems => (await getCountQuery(elems)).aggregate.count,
|
|
127
|
+
getPaginated: selectQueryPaginated,
|
|
128
|
+
getPaginatedWithCount: async elems => {
|
|
129
|
+
const elemsWithCount = await selectQueryPaginatedWithCount.all(elems)
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
[name]: elemsWithCount[name],
|
|
133
|
+
count: elemsWithCount[`${name}_aggregate`].aggregate.count,
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const get = _ => Object.values(_)[0]
|
|
2
|
+
const getAll = _ => _
|
|
3
|
+
const getOne = _ => Object.values(_)[0][0]
|
|
4
|
+
export const prepare = ({ runFromString, subscribeFromString }, query) => {
|
|
5
|
+
if (typeof query !== 'string') {
|
|
6
|
+
throw Error(`Query must be a string but was ${typeof query}`)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let [_, type, name] = query.split(/^\s*(\w+)(?:\s+(\w+))?\b/)
|
|
10
|
+
if (!type) {
|
|
11
|
+
type = 'query'
|
|
12
|
+
} else if (!/^(subscription|mutation|query)$/.test(type)) {
|
|
13
|
+
throw Error(`Invalid query, type must be query, mutation or subscription`)
|
|
14
|
+
}
|
|
15
|
+
name || (name = `${type}_${query.split(/{\s*(.+?)\b/)[1]}`)
|
|
16
|
+
const payload = JSON.stringify({ query })
|
|
17
|
+
const noVars = payload
|
|
18
|
+
const base = payload.slice(0, -1)
|
|
19
|
+
const build = vars => {
|
|
20
|
+
if (!vars) return noVars
|
|
21
|
+
if (typeof vars === 'function') {
|
|
22
|
+
throw Error(
|
|
23
|
+
'variables should not be functions, verify the order of your parameters',
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
const stringified = JSON.stringify(vars)
|
|
27
|
+
if (stringified === '{}') return noVars
|
|
28
|
+
return `${base},"variables":${stringified}}`
|
|
29
|
+
}
|
|
30
|
+
const map =
|
|
31
|
+
type === 'subscription'
|
|
32
|
+
? mapper => (sub, vars) =>
|
|
33
|
+
subscribeFromString(value => sub(mapper(value)), build(vars), name)
|
|
34
|
+
: mapper => async vars => mapper(await runFromString(build(vars), name))
|
|
35
|
+
|
|
36
|
+
const run = map(get)
|
|
37
|
+
run.all = map(getAll)
|
|
38
|
+
run.one = map(getOne)
|
|
39
|
+
run.map = map
|
|
40
|
+
run.query = query
|
|
41
|
+
return run
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const initPrepare = client => query => prepare(client, query)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@01-edu/shared",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "",
|
|
6
6
|
"scripts": {
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
"./attrs.js",
|
|
14
14
|
"./attrs-defs.js",
|
|
15
15
|
"./graph.js",
|
|
16
|
+
"./hasura-core.js",
|
|
17
|
+
"./hasura-model.js",
|
|
18
|
+
"./hasura-prepare.js",
|
|
16
19
|
"./languages.js",
|
|
17
20
|
"./definitions-checker.js",
|
|
18
21
|
"./path.js",
|
|
@@ -20,8 +23,12 @@
|
|
|
20
23
|
"./score.js",
|
|
21
24
|
"./skill-definitions.js",
|
|
22
25
|
"./toolbox.js",
|
|
23
|
-
"./object-structure.
|
|
26
|
+
"./object-structure.js",
|
|
27
|
+
"./qa-utils.js"
|
|
24
28
|
],
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"esbuild": "^0.25.12"
|
|
31
|
+
},
|
|
25
32
|
"license": "Fair",
|
|
26
33
|
"bin": {
|
|
27
34
|
"check-defs": "./bin/check-definitions.js"
|
package/qa-utils.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const stringifyJSX = value => {
|
|
2
|
+
if (!value) return ''
|
|
3
|
+
if (typeof value === 'string') return value
|
|
4
|
+
if (Array.isArray(value)) return value.map(stringifyJSX).join('')
|
|
5
|
+
return stringifyJSX(value.props?.children)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const toTestId = value =>
|
|
9
|
+
stringifyJSX(value)
|
|
10
|
+
.toLowerCase()
|
|
11
|
+
.replace(/([^a-z0-9]+)/g, ' ')
|
|
12
|
+
.trim()
|
|
13
|
+
.replaceAll(' ', '-')
|
package/object-structure.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
export const onboardingTypes = new Set([
|
|
2
|
-
'onboarding',
|
|
3
|
-
'piscine-registration',
|
|
4
|
-
'interview',
|
|
5
|
-
'games',
|
|
6
|
-
'administration',
|
|
7
|
-
'module-registration',
|
|
8
|
-
'form-step',
|
|
9
|
-
'sign-step',
|
|
10
|
-
'upload-step',
|
|
11
|
-
'contact-validation-step',
|
|
12
|
-
'avatar-step',
|
|
13
|
-
] as const)
|
|
14
|
-
|
|
15
|
-
type Extract<T> = [T] extends [Set<infer V>] ? V : never
|
|
16
|
-
|
|
17
|
-
export const objectTypes = new Set([
|
|
18
|
-
'module',
|
|
19
|
-
'piscine',
|
|
20
|
-
'exam',
|
|
21
|
-
'raid',
|
|
22
|
-
'quest',
|
|
23
|
-
'exercise',
|
|
24
|
-
'project',
|
|
25
|
-
'signup',
|
|
26
|
-
'campus',
|
|
27
|
-
...onboardingTypes,
|
|
28
|
-
] as const)
|
|
29
|
-
|
|
30
|
-
export type ObjectType = Extract<typeof objectTypes>
|
|
31
|
-
export const childTypes = {
|
|
32
|
-
campus: ['signup', 'onboarding', 'piscine', 'module'],
|
|
33
|
-
signup: [
|
|
34
|
-
'form-step',
|
|
35
|
-
'sign-step',
|
|
36
|
-
'upload-step',
|
|
37
|
-
'contact-validation-step',
|
|
38
|
-
'avatar-step',
|
|
39
|
-
],
|
|
40
|
-
onboarding: [
|
|
41
|
-
'games',
|
|
42
|
-
'administration',
|
|
43
|
-
'interview',
|
|
44
|
-
'piscine-registration',
|
|
45
|
-
'module-registration',
|
|
46
|
-
],
|
|
47
|
-
administration: [
|
|
48
|
-
'form-step',
|
|
49
|
-
'sign-step',
|
|
50
|
-
'upload-step',
|
|
51
|
-
'contact-validation-step',
|
|
52
|
-
'avatar-step',
|
|
53
|
-
],
|
|
54
|
-
piscine: ['quest', 'exam', 'raid', 'project'],
|
|
55
|
-
exam: ['exercise'],
|
|
56
|
-
quest: ['exercise'],
|
|
57
|
-
module: ['project', 'piscine', 'exam'],
|
|
58
|
-
} as const satisfies Partial<Record<ObjectType, ObjectType[]>>
|
|
59
|
-
|
|
60
|
-
export type ChildTypes = typeof childTypes
|