@01-edu/shared 2.0.5 → 2.0.6
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/dist/attrs-defs.js +1 -0
- package/dist/attrs.js +1 -0
- package/dist/bin/check-definitions.js +3 -0
- package/dist/chunk-BLXMKMNK.js +1 -0
- package/dist/chunk-FQWECXED.js +1 -0
- package/dist/chunk-IF63VBJO.js +1 -0
- package/dist/chunk-K5Z4W5GV.js +1 -0
- package/dist/chunk-MV3DQ3PZ.js +1 -0
- package/dist/chunk-NR2KVFFU.js +1 -0
- package/dist/chunk-T2KIY67K.js +1 -0
- package/dist/chunk-YJMXNRLI.js +1 -0
- package/dist/chunk-ZI7IBRHE.js +1 -0
- package/dist/definitions-checker.js +1 -0
- package/dist/event-utils.js +1 -0
- package/dist/games-utils.js +1 -0
- package/dist/graph.js +1 -0
- package/dist/hasura-core.js +1 -0
- package/dist/hasura-model.js +34 -0
- package/dist/hasura-prepare.js +1 -0
- package/dist/modular-steps-utils.js +1 -0
- package/dist/object-structure.js +1 -0
- package/dist/onboarding.js +1 -0
- package/dist/path.js +1 -0
- package/dist/qa-utils.js +1 -0
- package/dist/score.js +1 -0
- package/dist/skill-definitions.js +1 -0
- package/dist/toolbox.js +1 -0
- package/package.json +17 -6
- package/attrs-defs.js +0 -4273
- package/attrs.js +0 -423
- package/bin/check-definitions.js +0 -74
- package/definitions-checker.js +0 -233
- package/event-utils.js +0 -58
- package/graph.js +0 -96
- package/hasura-core.js +0 -217
- package/hasura-model.js +0 -138
- package/hasura-prepare.js +0 -44
- package/languages.js +0 -147
- package/onboarding.js +0 -25
- package/path.js +0 -73
- package/programming-languages.js +0 -21
- package/qa-utils.js +0 -13
- package/score.js +0 -80
- package/skill-definitions.js +0 -359
- package/toolbox.js +0 -532
package/toolbox.js
DELETED
|
@@ -1,532 +0,0 @@
|
|
|
1
|
-
import { isFinished } from './event-utils.js'
|
|
2
|
-
|
|
3
|
-
const keyCodes = {
|
|
4
|
-
13: ({ save, value, event, allowLineBreak }) => {
|
|
5
|
-
if (event.shiftKey || allowLineBreak) return false
|
|
6
|
-
return save(value)
|
|
7
|
-
}, // enter
|
|
8
|
-
27: ({ set, originalValue }) => set(originalValue), // esc
|
|
9
|
-
83: ({ save, value, event }) =>
|
|
10
|
-
event.metaKey || event.ctrlKey ? save(value) : false, // cmd+s
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export const handleKeyDownEvent = args => {
|
|
14
|
-
const handler = keyCodes[args.event.keyCode]
|
|
15
|
-
|
|
16
|
-
if (handler) {
|
|
17
|
-
if (handler(args) !== false) {
|
|
18
|
-
args.event.preventDefault()
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export const last = array => array[array.length - 1]
|
|
24
|
-
|
|
25
|
-
export const upperFirst = str =>
|
|
26
|
-
str ? str[0].toUpperCase() + str.slice(1) : ''
|
|
27
|
-
|
|
28
|
-
export const replaceMap = (arr, elem, mapper) =>
|
|
29
|
-
arr.map(e => (e === elem ? mapper(e) : e))
|
|
30
|
-
|
|
31
|
-
// DATE & TIME
|
|
32
|
-
|
|
33
|
-
export const SEC = 1000
|
|
34
|
-
export const MIN = 60 * SEC
|
|
35
|
-
export const HOUR = 60 * MIN
|
|
36
|
-
export const DAY = 24 * HOUR
|
|
37
|
-
export const WEEK = 7 * DAY
|
|
38
|
-
export const YEAR = 365 * DAY
|
|
39
|
-
|
|
40
|
-
const unitsOfTime = Object.entries({
|
|
41
|
-
year: YEAR,
|
|
42
|
-
month: 2628000000,
|
|
43
|
-
day: DAY,
|
|
44
|
-
hour: HOUR,
|
|
45
|
-
minute: MIN,
|
|
46
|
-
second: SEC,
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
export const units = {
|
|
50
|
-
years: YEAR,
|
|
51
|
-
year: YEAR,
|
|
52
|
-
yrs: YEAR,
|
|
53
|
-
yr: YEAR,
|
|
54
|
-
y: YEAR,
|
|
55
|
-
|
|
56
|
-
weeks: WEEK,
|
|
57
|
-
week: WEEK,
|
|
58
|
-
w: WEEK,
|
|
59
|
-
|
|
60
|
-
days: DAY,
|
|
61
|
-
day: DAY,
|
|
62
|
-
d: DAY,
|
|
63
|
-
|
|
64
|
-
hours: HOUR,
|
|
65
|
-
hour: HOUR,
|
|
66
|
-
hrs: HOUR,
|
|
67
|
-
hr: HOUR,
|
|
68
|
-
h: HOUR,
|
|
69
|
-
|
|
70
|
-
minutes: MIN,
|
|
71
|
-
minute: MIN,
|
|
72
|
-
mins: MIN,
|
|
73
|
-
min: MIN,
|
|
74
|
-
m: MIN,
|
|
75
|
-
|
|
76
|
-
seconds: SEC,
|
|
77
|
-
second: SEC,
|
|
78
|
-
secs: SEC,
|
|
79
|
-
sec: SEC,
|
|
80
|
-
s: SEC,
|
|
81
|
-
|
|
82
|
-
milliseconds: 1,
|
|
83
|
-
millisecond: 1,
|
|
84
|
-
ms: 1,
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const rtf = new Intl.RelativeTimeFormat('en', { style: 'narrow' })
|
|
88
|
-
export const relativeTime = elapsed => {
|
|
89
|
-
for (const [unit, amount] of unitsOfTime) {
|
|
90
|
-
if (Math.abs(elapsed) > amount || unit === 'second') {
|
|
91
|
-
return rtf.format(Math.round(elapsed / amount), unit)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export const padStart0 = s => s.toString().padStart(2, '0')
|
|
97
|
-
|
|
98
|
-
export const timeToMS = str => {
|
|
99
|
-
if (typeof str !== 'string') return 0
|
|
100
|
-
str = str.toLowerCase()
|
|
101
|
-
const matcher = /(?<value>[0-9.-]+) *(?<type>[a-z]+)/g
|
|
102
|
-
let match
|
|
103
|
-
let result = 0
|
|
104
|
-
while ((match = matcher.exec(str))) {
|
|
105
|
-
const { type, value } = match.groups
|
|
106
|
-
const unit = units[type]
|
|
107
|
-
const n = Number(value)
|
|
108
|
-
n && unit && (result += value * unit)
|
|
109
|
-
}
|
|
110
|
-
return Math.round(result)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// TODO: formated should be written formatted
|
|
114
|
-
export const formatedDurationColons = (seconds, { hoursOnly } = {}) => {
|
|
115
|
-
const d = !hoursOnly && Math.floor(seconds / 86400)
|
|
116
|
-
const h = Math.floor((!hoursOnly ? seconds % 86400 : seconds) / 3600)
|
|
117
|
-
const min = Math.floor((seconds % 3600) / 60)
|
|
118
|
-
const sec = Math.floor(seconds % 60)
|
|
119
|
-
const time = `${padStart0(h)}:${padStart0(min)}:${padStart0(sec)}`
|
|
120
|
-
return d ? `${d}d ${time}` : time
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export const formatedMS = ms => formatedDuration(Math.round(ms / SEC))
|
|
124
|
-
export const timeUnits = [
|
|
125
|
-
{ label: 'y', fullLabel: 'year', size: 31536000 },
|
|
126
|
-
{ label: 'w', fullLabel: 'week', size: 604800 },
|
|
127
|
-
{ label: 'd', fullLabel: 'day', size: 86400 },
|
|
128
|
-
{ label: 'h', fullLabel: 'hour', size: 3600 },
|
|
129
|
-
{ label: 'm', fullLabel: 'minute', size: 60 },
|
|
130
|
-
{ label: 's', fullLabel: 'second', size: 1 },
|
|
131
|
-
]
|
|
132
|
-
export const formatedDuration = (
|
|
133
|
-
seconds,
|
|
134
|
-
{ noSeconds, stopFirst, useFullLabel, stopLabel } = {},
|
|
135
|
-
) => {
|
|
136
|
-
const parts = []
|
|
137
|
-
if (
|
|
138
|
-
stopLabel &&
|
|
139
|
-
seconds < timeUnits.find(u => u.fullLabel === stopLabel)?.size
|
|
140
|
-
)
|
|
141
|
-
return `less than 1${useFullLabel ? ` ${stopLabel}` : timeUnits.find(u => u.fullLabel === stopLabel).label}`
|
|
142
|
-
for (const { label, size, fullLabel } of timeUnits) {
|
|
143
|
-
const value = Math.floor(seconds / size)
|
|
144
|
-
if (value !== 0) {
|
|
145
|
-
parts.push(
|
|
146
|
-
`${value}${useFullLabel ? ` ${fullLabel}` : label}${value > 1 && useFullLabel ? 's' : ''}`,
|
|
147
|
-
)
|
|
148
|
-
if (stopFirst) return parts
|
|
149
|
-
seconds -= value * size
|
|
150
|
-
}
|
|
151
|
-
if (fullLabel === stopLabel) {
|
|
152
|
-
return parts.join(' ') || (noSeconds ? '0m' : '0s')
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
if (!noSeconds && seconds !== 0) {
|
|
156
|
-
parts.push(`${seconds}s`)
|
|
157
|
-
}
|
|
158
|
-
return parts.join(' ') || (noSeconds ? '0m' : '0s')
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Returns the difference between two dates in the following format: "3 months"
|
|
162
|
-
// Always returns the largest unit only
|
|
163
|
-
// If only one date is provided, it returns the difference between that date and now, the value is absolute
|
|
164
|
-
export const timeDiff = (date1, date2 = Date.now()) => {
|
|
165
|
-
if (!date1) return null
|
|
166
|
-
const diffMs = toDate(date1) - toDate(date2)
|
|
167
|
-
return formatedDuration(Math.floor(Math.abs(diffMs) / 1000), {
|
|
168
|
-
stopFirst: true,
|
|
169
|
-
useFullLabel: true,
|
|
170
|
-
})
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const toDate = date => (date instanceof Date ? date : new Date(date))
|
|
174
|
-
export const toDateFormat = (d, isISO = true) => {
|
|
175
|
-
if (dateIsPermanent(d)) return '∞'
|
|
176
|
-
const date = toDate(d)
|
|
177
|
-
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
|
178
|
-
const day = date.getDate().toString().padStart(2, '0')
|
|
179
|
-
|
|
180
|
-
return isISO
|
|
181
|
-
? `${date.getFullYear()}-${month}-${day}`
|
|
182
|
-
: `${day}/${month}/${date.getFullYear()}`
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
export const toISOStringWithTimeZone = d => toDate(d).toISOString()
|
|
186
|
-
|
|
187
|
-
export const toDateFormatWithTime = (d, separator = 'T') => {
|
|
188
|
-
const date = toDate(d)
|
|
189
|
-
const hh = String(date.getHours()).padStart(2, '0')
|
|
190
|
-
const mm = String(date.getMinutes()).padStart(2, '0')
|
|
191
|
-
|
|
192
|
-
return `${toDateFormat(date)}${separator}${hh}:${mm}`
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
export const toPermanentDateFormatWithTime = (d, separator = 'T') => {
|
|
196
|
-
const date = toDate(d)
|
|
197
|
-
const hh = String(date.getHours()).padStart(2, '0')
|
|
198
|
-
const mm = String(date.getMinutes()).padStart(2, '0')
|
|
199
|
-
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
|
200
|
-
const day = date.getDate().toString().padStart(2, '0')
|
|
201
|
-
|
|
202
|
-
return `${date.getFullYear()}-${month}-${day}${separator}${hh}:${mm}`
|
|
203
|
-
}
|
|
204
|
-
export const displayDateTime = dateTime => toDateFormatWithTime(dateTime, ' | ')
|
|
205
|
-
|
|
206
|
-
export const getMonthName = month => monthNames[month]
|
|
207
|
-
|
|
208
|
-
export const getDayName = day => weekDays[day]
|
|
209
|
-
|
|
210
|
-
export const monthNames = [
|
|
211
|
-
'Jan',
|
|
212
|
-
'Feb',
|
|
213
|
-
'Mar',
|
|
214
|
-
'Apr',
|
|
215
|
-
'May',
|
|
216
|
-
'Jun',
|
|
217
|
-
'Jul',
|
|
218
|
-
'Aug',
|
|
219
|
-
'Sep',
|
|
220
|
-
'Oct',
|
|
221
|
-
'Nov',
|
|
222
|
-
'Dec',
|
|
223
|
-
]
|
|
224
|
-
|
|
225
|
-
export const weekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
|
226
|
-
|
|
227
|
-
const hour = hour => {
|
|
228
|
-
if (hour === 0 || hour === 12) return 12
|
|
229
|
-
return hour % 12
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const minPad = min => String(min).padStart(2, '0')
|
|
233
|
-
|
|
234
|
-
const suffix = hour => {
|
|
235
|
-
if (hour === 0) return 'am'
|
|
236
|
-
if (hour === 12) return 'pm'
|
|
237
|
-
return hour < 12 ? 'am' : 'pm'
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
export const dateIsPermanent = endAt => {
|
|
241
|
-
const date =
|
|
242
|
-
endAt instanceof Date && !Number.isNaN(endAt.valueOf())
|
|
243
|
-
? endAt
|
|
244
|
-
: new Date(endAt)
|
|
245
|
-
|
|
246
|
-
// 32503683600000 = year 3000 timestamp
|
|
247
|
-
return date.valueOf() > 32503683600000
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const getDateElems = date => {
|
|
251
|
-
const d = new Date(date)
|
|
252
|
-
const day = d.getDay()
|
|
253
|
-
const jj = d.getDate()
|
|
254
|
-
const mm = d.getMonth()
|
|
255
|
-
const yyyy = d.getFullYear()
|
|
256
|
-
const hh = d.getHours()
|
|
257
|
-
const min = d.getMinutes()
|
|
258
|
-
return { day, jj, mm, yyyy, hh, min }
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
export const formatedDateTime = (date, splitter = 'at') => {
|
|
262
|
-
if (!date) return '-'
|
|
263
|
-
if (dateIsPermanent(date)) return '∞'
|
|
264
|
-
const { jj, mm, yyyy, hh, min } = getDateElems(date)
|
|
265
|
-
|
|
266
|
-
return `${monthNames[mm]} ${jj}, ${yyyy} ${splitter} ${hour(hh)}:${minPad(
|
|
267
|
-
min,
|
|
268
|
-
)} ${suffix(hh)}`
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
export const formatedDateTimeWithDay = date => {
|
|
272
|
-
if (!date) return '-'
|
|
273
|
-
if (dateIsPermanent(date)) return '∞'
|
|
274
|
-
const { day, jj, mm, yyyy, hh, min } = getDateElems(date)
|
|
275
|
-
|
|
276
|
-
return `${getDayName(day)}, ${monthNames[mm]} ${jj}, ${yyyy} at ${hour(
|
|
277
|
-
hh,
|
|
278
|
-
)}:${minPad(min)} ${suffix(hh)}`
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
export const formatedDate = date => {
|
|
282
|
-
if (!date) return '-'
|
|
283
|
-
if (dateIsPermanent(date)) return '∞'
|
|
284
|
-
return toDateFormat(date)
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
export const defer = () => {
|
|
288
|
-
const result = {}
|
|
289
|
-
result.done = new Promise((resolve, reject) => {
|
|
290
|
-
result.resolve = resolve
|
|
291
|
-
result.reject = reject
|
|
292
|
-
})
|
|
293
|
-
return result
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// slugify('A Test') => a-test
|
|
297
|
-
export const slugify = str =>
|
|
298
|
-
str
|
|
299
|
-
.normalize('NFKD')
|
|
300
|
-
.replace(/\p{M}+/gu, '')
|
|
301
|
-
.toLowerCase()
|
|
302
|
-
.replace(/\s+/g, ' ')
|
|
303
|
-
.replace(/[^a-z0-9._ ]+/g, '')
|
|
304
|
-
.trim()
|
|
305
|
-
.replaceAll(' ', '-')
|
|
306
|
-
|
|
307
|
-
export const fullName = ({ firstName, lastName }) =>
|
|
308
|
-
`${firstName || ''} ${lastName || ''}`
|
|
309
|
-
|
|
310
|
-
export const formatTime = t =>
|
|
311
|
-
new Date(t)
|
|
312
|
-
.toISOString()
|
|
313
|
-
.substr(11, 8)
|
|
314
|
-
.replace(/^[0:]+/, '')
|
|
315
|
-
|
|
316
|
-
export const wait = delay => new Promise(resolve => setTimeout(resolve, delay))
|
|
317
|
-
|
|
318
|
-
const lowertalize = str => str[0].toLowerCase() + str.slice(1)
|
|
319
|
-
const upperFirstMatch = (_, firstMatch) => firstMatch.toUpperCase()
|
|
320
|
-
export const toCamelCase = str =>
|
|
321
|
-
lowertalize(
|
|
322
|
-
str
|
|
323
|
-
.trim()
|
|
324
|
-
.toLowerCase()
|
|
325
|
-
.replace(/[ _-]+(.?)/g, upperFirstMatch),
|
|
326
|
-
)
|
|
327
|
-
export const isNotCamel = str => /[ _-]+(.?)/g.test(str)
|
|
328
|
-
|
|
329
|
-
const underscoreJoin = (_, a, b) => `${a}_${b}`
|
|
330
|
-
const toSnakeCase = str =>
|
|
331
|
-
str
|
|
332
|
-
.trim()
|
|
333
|
-
.replace(/(.)([A-Z])/g, underscoreJoin)
|
|
334
|
-
.toLowerCase()
|
|
335
|
-
|
|
336
|
-
// seems to match all cases:
|
|
337
|
-
// 'FOO-BAR_01Chart ki' > f-o-o-b-a-r-01-chart-ki
|
|
338
|
-
// phant0m-writ3r > pass
|
|
339
|
-
// phoneValidation > phone-validation
|
|
340
|
-
// CHart01 > c-hart01
|
|
341
|
-
// addtionnal_info > addtionnal-info
|
|
342
|
-
// id card > id-card
|
|
343
|
-
// should be enough for unique usage we have of it in check-refs
|
|
344
|
-
// checked the perfs and it is faster + more secure than using replace(s) + toLowerCase
|
|
345
|
-
export const toKebabCase = str =>
|
|
346
|
-
str
|
|
347
|
-
.match(
|
|
348
|
-
/[A-Z](?=[A-Z][a-z]+[0-9]*[a-z]|\b)|[A-Z]?[a-z]+[0-9]+[a-z]?[A-Z]*|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g,
|
|
349
|
-
)
|
|
350
|
-
.map(x => x.toLowerCase())
|
|
351
|
-
.join('-')
|
|
352
|
-
|
|
353
|
-
const spaceJoin = (_, a, b) => `${a} ${b}`
|
|
354
|
-
export const breath = str =>
|
|
355
|
-
str
|
|
356
|
-
.trim()
|
|
357
|
-
.replace(/(.)([A-Z])/g, spaceJoin)
|
|
358
|
-
.toLowerCase()
|
|
359
|
-
export const prettyKey = key => upperFirst(breath(key))
|
|
360
|
-
|
|
361
|
-
const switchKeys = (object, switchCase, switchAll) =>
|
|
362
|
-
mapEntries(object, ([key, value]) => [switchCase(key), switchAll(value)])
|
|
363
|
-
|
|
364
|
-
export const toCamelKeys = object => switchKeys(object, toCamelCase, toCamelAll)
|
|
365
|
-
export const toSnakeKeys = object => switchKeys(object, toSnakeCase, toSnakeAll)
|
|
366
|
-
|
|
367
|
-
const switchAll = (elem, switchCase, switchKeys, toSwitchAll) => {
|
|
368
|
-
if (!elem || typeof elem !== 'object') return elem
|
|
369
|
-
return Array.isArray(elem)
|
|
370
|
-
? elem.map(toSwitchAll)
|
|
371
|
-
: switchKeys(elem, switchCase, toSwitchAll)
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
export const toCamelAll = elem =>
|
|
375
|
-
switchAll(elem, toCamelCase, toCamelKeys, toCamelAll)
|
|
376
|
-
|
|
377
|
-
export const toSnakeAll = elem =>
|
|
378
|
-
switchAll(elem, toSnakeCase, toSnakeKeys, toSnakeAll)
|
|
379
|
-
|
|
380
|
-
export const arrayOf = (size, start = 0) =>
|
|
381
|
-
[...Array(size + start).keys()].slice(start)
|
|
382
|
-
|
|
383
|
-
export const trimSlashEnd = path =>
|
|
384
|
-
path[path.length - 1] === '/' ? trimSlashEnd(path.slice(0, -1)) : path
|
|
385
|
-
|
|
386
|
-
export const mapEntries = (obj, fn) =>
|
|
387
|
-
Object.fromEntries(Object.entries(obj).map(fn))
|
|
388
|
-
|
|
389
|
-
export const mapValues = (obj, fn) =>
|
|
390
|
-
mapEntries(obj, ([k, v]) => [k, fn(v, k, obj)])
|
|
391
|
-
|
|
392
|
-
export const filterEntries = (obj, fn) =>
|
|
393
|
-
Object.fromEntries(Object.entries(obj).filter(fn))
|
|
394
|
-
|
|
395
|
-
export const filterValues = (obj, fn) =>
|
|
396
|
-
filterEntries(obj, ([k, v]) => [k, fn(v, k, obj)])
|
|
397
|
-
|
|
398
|
-
export const getChanges = (newObject, refObject) =>
|
|
399
|
-
filterEntries(newObject, ([key, value]) => !eq(value, refObject[key]))
|
|
400
|
-
|
|
401
|
-
export const hasChanged = (newObject, refObject) =>
|
|
402
|
-
Object.keys(getChanges(newObject, refObject)).length !== 0
|
|
403
|
-
|
|
404
|
-
export const shuffle = arr => {
|
|
405
|
-
const newArr = [...arr]
|
|
406
|
-
let i = newArr.length
|
|
407
|
-
let j, tmp
|
|
408
|
-
while (--i > 0) {
|
|
409
|
-
j = Math.floor(Math.random() * (i + 1))
|
|
410
|
-
tmp = newArr[j]
|
|
411
|
-
newArr[j] = newArr[i]
|
|
412
|
-
newArr[i] = tmp
|
|
413
|
-
}
|
|
414
|
-
return newArr
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
// specific to data structure
|
|
418
|
-
export const getChildByType = (obj, childType) =>
|
|
419
|
-
obj && Object.values(obj).find(child => child.type === childType)
|
|
420
|
-
|
|
421
|
-
export const getUserName = user => {
|
|
422
|
-
if (!user) return '-'
|
|
423
|
-
if (user.attrs?.name) return user.attrs.name
|
|
424
|
-
const firstName = user.firstName || '-'
|
|
425
|
-
const lastName = user.lastName || '-'
|
|
426
|
-
|
|
427
|
-
return `${firstName} ${lastName}`
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// This only works for JSON like value
|
|
431
|
-
// no support for Dates, Regexp, Set, Map etc...
|
|
432
|
-
// this is 100% fine for anything from the database
|
|
433
|
-
export const eq = (a, b) => {
|
|
434
|
-
if (a === b) return true
|
|
435
|
-
if (typeof a !== typeof b) return false
|
|
436
|
-
if (typeof a === 'number' && Number.isNaN(a) && Number.isNaN(b)) return true
|
|
437
|
-
if (typeof a === 'object') {
|
|
438
|
-
if (!a || !b) return false
|
|
439
|
-
if (a.constructor !== b.constructor) return false
|
|
440
|
-
const entries = Object.entries(a)
|
|
441
|
-
if (entries.length !== Object.values(b).length) return false
|
|
442
|
-
for (const [k, v] of entries) {
|
|
443
|
-
if (!eq(b[k], v)) return false
|
|
444
|
-
}
|
|
445
|
-
return true
|
|
446
|
-
}
|
|
447
|
-
return false
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
export const getStatus = progress => {
|
|
451
|
-
if (!progress || !progress.isDone) return
|
|
452
|
-
return progress.succeeded ? 'succeeded' : 'failed'
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
const isAccessBlocked = record =>
|
|
456
|
-
!record.type.canAccessPlatform &&
|
|
457
|
-
(new Date(record.endAt) > Date.now() ||
|
|
458
|
-
record.type.isPermanent ||
|
|
459
|
-
!record.endAt) &&
|
|
460
|
-
isFinished(record.startAt)
|
|
461
|
-
|
|
462
|
-
// TODO: could maybe check user.public.canAccessPlatform?
|
|
463
|
-
export const isBanned = user => {
|
|
464
|
-
const records = user?.records
|
|
465
|
-
const record = records?.find(isAccessBlocked)
|
|
466
|
-
if (!record) return
|
|
467
|
-
const err = Error('Banned')
|
|
468
|
-
err.record = record
|
|
469
|
-
return err
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
export const generateCode = () =>
|
|
473
|
-
Math.random()
|
|
474
|
-
.toString(36)
|
|
475
|
-
.slice(2, 7)
|
|
476
|
-
.padEnd(5, '#')
|
|
477
|
-
.replace(/0/gi, '_')
|
|
478
|
-
.replace(/o/gi, '-')
|
|
479
|
-
.replace(/I/gi, '?')
|
|
480
|
-
.replace(/l/gi, '$')
|
|
481
|
-
|
|
482
|
-
export const base64Encode = str => {
|
|
483
|
-
const encoder = new TextEncoder()
|
|
484
|
-
const data = encoder.encode(str)
|
|
485
|
-
return btoa(String.fromCharCode.apply(null, data))
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
export const timePlusDelay = (time, delay) =>
|
|
489
|
-
new Date(new Date(time).getTime() + delay).toISOString()
|
|
490
|
-
|
|
491
|
-
export const getRecordStatus = record => {
|
|
492
|
-
if (!isFinished(record?.startAt)) return 'starting soon'
|
|
493
|
-
if (record?.endAt && isFinished(record?.endAt)) {
|
|
494
|
-
return 'finished'
|
|
495
|
-
}
|
|
496
|
-
if (record?.type.isPermanent) return 'permanent'
|
|
497
|
-
return record?.endAt ? 'in progress' : 'unblock required'
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
export const createFrequencyMap = arr =>
|
|
501
|
-
arr.reduce((map, item) => {
|
|
502
|
-
map[item] = (map[item] || 0) + 1
|
|
503
|
-
return map
|
|
504
|
-
}, {})
|
|
505
|
-
|
|
506
|
-
export const postgresIntervalToMS = intervalStr => {
|
|
507
|
-
if (!intervalStr) return 0
|
|
508
|
-
// Regex to capture years, months, days, hours, minutes, seconds
|
|
509
|
-
const regex =
|
|
510
|
-
/(?:(\d+)\s+years?)?\s*(?:(\d+)\s+mons?)?\s*(?:(\d+)\s+days?)?\s*(?:(\d+):(\d+):([\d.]+))?/
|
|
511
|
-
const match = intervalStr.match(regex)
|
|
512
|
-
|
|
513
|
-
if (!match) return 0
|
|
514
|
-
|
|
515
|
-
const years = Number.parseInt(match[1] || 0, 10)
|
|
516
|
-
const months = Number.parseInt(match[2] || 0, 10)
|
|
517
|
-
const days = Number.parseInt(match[3] || 0, 10)
|
|
518
|
-
const hours = Number.parseInt(match[4] || 0, 10)
|
|
519
|
-
const minutes = Number.parseInt(match[5] || 0, 10)
|
|
520
|
-
const seconds = Number.parseFloat(match[6] || 0)
|
|
521
|
-
|
|
522
|
-
// Convert everything to milliseconds
|
|
523
|
-
const ms =
|
|
524
|
-
years * 365 * 24 * 60 * 60 * 1000 + // assume 1 year = 365 days
|
|
525
|
-
months * 30 * 24 * 60 * 60 * 1000 + // assume 1 month = 30 days
|
|
526
|
-
days * 24 * 60 * 60 * 1000 +
|
|
527
|
-
hours * 60 * 60 * 1000 +
|
|
528
|
-
minutes * 60 * 1000 +
|
|
529
|
-
seconds * 1000
|
|
530
|
-
|
|
531
|
-
return ms
|
|
532
|
-
}
|