@es-labs/jslib 0.0.1

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 (72) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +42 -0
  3. package/__test__/services.test.js +32 -0
  4. package/auth/index.js +226 -0
  5. package/auth/keyv.js +23 -0
  6. package/auth/knex.js +29 -0
  7. package/auth/redis.js +23 -0
  8. package/comms/email.js +123 -0
  9. package/comms/nexmo.js +44 -0
  10. package/comms/telegram.js +43 -0
  11. package/comms/telegram2/inbound.js +314 -0
  12. package/comms/telegram2/outbound.js +574 -0
  13. package/comms/webpush.js +60 -0
  14. package/config.js +37 -0
  15. package/express/controller/auth/oauth.js +39 -0
  16. package/express/controller/auth/oidc.js +87 -0
  17. package/express/controller/auth/own.js +100 -0
  18. package/express/controller/auth/saml.js +74 -0
  19. package/express/upload.js +48 -0
  20. package/index.js +1 -0
  21. package/iso/README.md +4 -0
  22. package/iso/__tests__/csv-utils.spec.js +128 -0
  23. package/iso/__tests__/datetime.spec.js +101 -0
  24. package/iso/__tests__/fetch.spec.js +270 -0
  25. package/iso/csv-utils.js +206 -0
  26. package/iso/datetime.js +103 -0
  27. package/iso/fetch.js +129 -0
  28. package/iso/fetch2.js +180 -0
  29. package/iso/log-filter.js +17 -0
  30. package/iso/sleep.js +6 -0
  31. package/iso/ws.js +63 -0
  32. package/node/oss-files/oss-uploader-client-fetch.js +258 -0
  33. package/node/oss-files/oss-uploader-client-fetch.md +31 -0
  34. package/node/oss-files/oss-uploader-client.js +219 -0
  35. package/node/oss-files/oss-uploader-server.js +199 -0
  36. package/node/oss-files/oss-uploader-usage.js +121 -0
  37. package/node/oss-files/oss-uploader-usage.md +34 -0
  38. package/node/oss-files/s3-uploader-client.js +217 -0
  39. package/node/oss-files/s3-uploader-server.js +123 -0
  40. package/node/oss-files/s3-uploader-usage.js +77 -0
  41. package/node/oss-files/s3-uploader-usage.md +34 -0
  42. package/package.json +53 -0
  43. package/packageInfo.js +9 -0
  44. package/services/ali.js +279 -0
  45. package/services/aws.js +194 -0
  46. package/services/db/__tests__/keyv.spec.js +31 -0
  47. package/services/db/keyv.js +14 -0
  48. package/services/db/knex.js +67 -0
  49. package/services/db/redis.js +51 -0
  50. package/services/index.js +57 -0
  51. package/services/mq/README.md +8 -0
  52. package/services/websocket.js +139 -0
  53. package/t4t/README.md +1 -0
  54. package/traps.js +20 -0
  55. package/utils/__tests__/aes.spec.js +52 -0
  56. package/utils/aes.js +23 -0
  57. package/web/UI.md +71 -0
  58. package/web/bwc-autocomplete.js +211 -0
  59. package/web/bwc-combobox.js +343 -0
  60. package/web/bwc-fileupload.js +87 -0
  61. package/web/bwc-loading-overlay.js +54 -0
  62. package/web/bwc-t4t-form.js +511 -0
  63. package/web/bwc-table.js +756 -0
  64. package/web/fetch.js +129 -0
  65. package/web/i18n.js +24 -0
  66. package/web/idle.js +49 -0
  67. package/web/parse-jwt.js +15 -0
  68. package/web/pwa.js +84 -0
  69. package/web/sign-pad.js +164 -0
  70. package/web/t4t-fe.js +164 -0
  71. package/web/util.js +126 -0
  72. package/web/web-cam.js +182 -0
@@ -0,0 +1,511 @@
1
+ // TODO fix
2
+ // onsubmit --> multi select
3
+ // error messages on submit
4
+
5
+ // attributes
6
+ // - mode: add, edit
7
+ //
8
+ // properties
9
+ // - record
10
+ // - config (t4t config)
11
+ //
12
+ // methods
13
+ //
14
+ // events
15
+
16
+ //
17
+ // requires bwc-combobox, debounce, autocomplete
18
+ import './bwc-combobox.js'
19
+ import { debounce } from './util.js'
20
+ import { autocomplete } from './t4t-fe.js'
21
+
22
+ const bulma = {
23
+ // the keys are from t4t cols.<col>.ui.tag
24
+ input: {
25
+ tag: 'div',
26
+ className: 'field',
27
+ children: [
28
+ { tag: 'label', className: 'label' },
29
+ { tag: 'div', className: 'control', children: [
30
+ { tag: 'input-placeholder', className: 'input' },
31
+ ] },
32
+ { tag: 'p', className: 'help is-danger', errorLabel: true }
33
+ ]
34
+ }, // end input
35
+ textarea: {
36
+ tag: 'div',
37
+ className: 'field',
38
+ children: [
39
+ { tag: 'label', className: 'label' },
40
+ { tag: 'div', className: 'control', children: [
41
+ { tag: 'input-placeholder', className: 'textarea' },
42
+ ] },
43
+ { tag: 'p', className: 'help is-danger', errorLabel: true }
44
+ ]
45
+ },
46
+ select: { // ugly multiple
47
+ tag: 'div',
48
+ className: 'field',
49
+ children: [
50
+ {
51
+ tag: 'div',
52
+ className: 'control',
53
+ children: [
54
+ { tag: 'label', className: 'label' },
55
+ {
56
+ tag: 'div',
57
+ className: 'select is-fullwidth', // need to add is-multiple for bulma
58
+ children: [
59
+ { tag: 'input-placeholder' },
60
+ ]
61
+ }
62
+ ]
63
+ }
64
+ ]
65
+ }, // end select
66
+ 'bwc-combobox': {
67
+ tag: 'div',
68
+ className: 'field',
69
+ children: [
70
+ { tag: 'label', className: 'label' },
71
+ { tag: 'div', className: 'control has-icons-left', children: [
72
+ { tag: 'input-placeholder' }, // TODO className = 'input'
73
+ ] },
74
+ { tag: 'p', className: 'help is-danger', errorLabel: true }
75
+ ]
76
+ }
77
+ } // end bulma
78
+
79
+ // Bootstrap - TODO VERIFY
80
+ const bootstrap = {
81
+ input: {
82
+ tag: 'div',
83
+ children: [
84
+ { tag: 'label', className: 'form-label' },
85
+ { tag: 'input-placeholder', className: 'form-control' },
86
+ { tag: 'div', className: 'form-text', errorLabel: true }
87
+ ]
88
+ },
89
+ textarea: {
90
+ tag: 'div',
91
+ children: [
92
+ { tag: 'label', className: 'form-label' },
93
+ { tag: 'input-placeholder', className: 'form-control' },
94
+ { tag: 'div', className: 'form-text', errorLabel: true }
95
+ ]
96
+ },
97
+ select: {
98
+ tag: 'div',
99
+ children: [
100
+ { tag: 'label', className: 'form-label' },
101
+ { tag: 'input-placeholder', className: 'form-select' },
102
+ ]
103
+ },
104
+ 'bwc-combobox': {
105
+ }
106
+ }
107
+
108
+ // Mui CSS - TODO VERIFY
109
+ const muicss = {
110
+ input: {
111
+ tag: 'div',
112
+ className: 'mui-textfield',
113
+ children: [
114
+ { tag: 'label', children: [ { tag: 'span', className: 'mui--text-danger', errorLabel: true } ] },
115
+ { tag: 'input-placeholder' },
116
+ ]
117
+ },
118
+ textarea: {
119
+ tag: 'div',
120
+ className: 'mui-textfield',
121
+ children: [
122
+ { tag: 'label', children: [ { tag: 'span', className: 'mui--text-danger', errorLabel: true } ] },
123
+ { tag: 'input-placeholder' },
124
+ ]
125
+ },
126
+ select: {
127
+ tag: 'div',
128
+ className: 'mui-select',
129
+ children: [
130
+ { tag: 'label' },
131
+ { tag: 'input-placeholder' },
132
+ ]
133
+ },
134
+ 'bwc-combobox': {
135
+ }
136
+ }
137
+
138
+ const framework = bulma // set as bulma first
139
+
140
+ const template = document.createElement('template')
141
+
142
+ template.innerHTML = /*html*/`
143
+ <style>
144
+ .input-widget {
145
+ /* background-color: red; */
146
+ display: flex;
147
+ flex-direction: row;
148
+ justify-content: center;
149
+ align-items: center;
150
+ height: var(--bwc-t4t-form-height, calc(100vh - 100px));
151
+ }
152
+ .form-area {
153
+ align-self: center;
154
+ /* background-color: lightgray; */
155
+ overflow: auto;
156
+ height: 96%;
157
+ width: 80%;
158
+ }
159
+ .top-area {
160
+ display: flex;
161
+ flex-direction: row;
162
+ justify-content: center;
163
+ align-items: center;
164
+
165
+ padding: 8px;
166
+ position: sticky;
167
+ top: 0px;
168
+ background-color: lightgray;
169
+ z-index: 1;
170
+ }
171
+ .bottom-area {
172
+ display: flex;
173
+ flex-direction: row;
174
+ justify-content: center;
175
+ align-items: center;
176
+
177
+ padding: 8px;
178
+ position: sticky;
179
+ bottom: 0px;
180
+ background-color: lightgray;
181
+ z-index: 1;
182
+ }
183
+ </style>
184
+ <div class="input-widget">
185
+ <form class="form-area" onsubmit="return false;">
186
+ <div class="top-area"><h1>Hello</h1></div>
187
+ <div class="content-area">
188
+ </div>
189
+ <div class="bottom-area">
190
+ <div class="button-group">
191
+ <!-- create buttons here -->
192
+ <button type="submit" class="btn-submit button is-link">Submit</button>
193
+ <button type="button" class="btn-cancel button is-link is-light">Cancel</button>
194
+ </div>
195
+ </div>
196
+ <form>
197
+ </div>
198
+ `
199
+
200
+ class BwcT4tForm extends HTMLElement {
201
+ constructor() {
202
+ super()
203
+ }
204
+
205
+ #config = [] // from table config property passed in
206
+ #record = {} // from record property passed in
207
+ #xcols = {} // extended column information - info on input element, event, etc...
208
+
209
+ get config () { return this.#config }
210
+ set config (val) { this.#config = val }
211
+
212
+ get record () { return this.#record }
213
+ set record (val) {
214
+ this.#record = val
215
+ if (this.#config && this.#record) {
216
+ // console.log('do render - val (this.#record)', val)
217
+ // console.log('do render - config', this.#config)
218
+ this._render()
219
+ }
220
+ }
221
+
222
+ connectedCallback() {
223
+ // console.log('bwc-t4t-form', this.#config, this.#record)
224
+ this.appendChild(template.content.cloneNode(true))
225
+ if (this.#config && this.#record) {
226
+ this._render()
227
+ }
228
+ }
229
+
230
+ static get observedAttributes() { return ['mode'] }
231
+
232
+ // attributeChangedCallback(name, oldVal, newVal) {
233
+ // switch (name) {
234
+ // case 'mode': break
235
+ // default: break
236
+ // }
237
+ // }
238
+
239
+ get mode() { return this.getAttribute('mode') }
240
+ set mode(val) { this.setAttribute('mode', val) }
241
+
242
+ // node is current node in tree, k = column key, c = column object
243
+ formEl (node, k, c) {
244
+ const mode = this.mode
245
+ if (c[mode] === 'hide') return null
246
+
247
+ // console.log(k, c)
248
+ const { tag, className, attrs, children, errorLabel } = node
249
+ // console.log(tag, className, attrs)
250
+ const elementTag = (tag === 'input-placeholder') ? c.ui.tag : tag // replace for this
251
+ const el = document.createElement(elementTag)
252
+
253
+ if (!this.#xcols[k]) this.#xcols[k] = { }
254
+
255
+ if (tag === 'label') el.innerText = c.label // set the label
256
+
257
+ const inputAttrs = c?.ui?.attrs // set col specific attributes for the input
258
+ if (inputAttrs) {
259
+ for (let key in inputAttrs) {
260
+ el.setAttribute(key, inputAttrs[key])
261
+ }
262
+ }
263
+
264
+ // DONE: input - text, integer, decimal, date, time, datetime, file(upload)
265
+ // DONE: select (single and multiple, limited options)
266
+ // DONE: textarea
267
+ // DONE: bwc-combobox (multiple with tags), TODO: need to test more
268
+
269
+ if (['input', 'textarea', 'select', 'bwc-combobox'].includes(elementTag)) { // its an input
270
+ if (c.mode === 'readonly') el.setAttribute('disabled', true) // select is disabled, as it applies to more html tags
271
+ if (c.required) el.setAttribute('required', true)
272
+
273
+ if (elementTag === 'select') { // set the options
274
+ // console.log('select', el.value, k, this.#record[k], this.mode)
275
+ const selectString = (this.mode === 'add') ? c.default || '' : this.#record[k] || ''
276
+ const selected = !selectString ? [] : (c?.ui?.attrs?.multiple) ? selectString.split(',') : [selectString]
277
+ const options = c?.ui?.options
278
+ for (let option of options) {
279
+ const optEl = document.createElement('option')
280
+ optEl.value = option.key
281
+ optEl.innerText = option.text
282
+ if (selected.includes(option.key)) {
283
+ optEl.selected = true // set selected
284
+ }
285
+ el.appendChild(optEl)
286
+ }
287
+ } else { // other input
288
+ if (elementTag === 'bwc-combobox') {
289
+ // console.log('bwc-combobox', this.#record)
290
+ el.setAttribute('listid', 'list-'+k)
291
+ el.setAttribute('object-key', 'key')
292
+ el.setAttribute('object-text', 'text')
293
+ if (c?.ui?.attrs?.multiple) el.setAttribute('multiple', '')
294
+ if (c?.ui?.attrs?.repeat) el.setAttribute('repeat', '')
295
+ if (c?.ui?.attrs?.allowCustomTag) el.setAttribute('allow-custom-tag', '')
296
+ if (c?.ui?.attrs?.tagLimit) el.setAttribute('tag-limit', c.ui.attrs.tagLimit)
297
+ // disbled and required already set
298
+ // TODO set input class
299
+
300
+ el.onload = (e) => { // need to wait for component to load before setting the values
301
+ // console.log('bwc loaded')
302
+ const valueType = c?.ui?.valueType // TODO transform value
303
+ if (c?.ui?.attrs?.multiple) { // can be array in column or join table
304
+ const val = this.mode === 'add' ? c.default : this.#record[k]
305
+ if (valueType === '') {
306
+ el.tags = val.split(',').map(item => ({ key: item, text: item })) || []
307
+ } else { // object
308
+ el.tags = val || []
309
+ }
310
+ } else { // single
311
+ const val = this.mode === 'add' ? c.default : this.#record[k]
312
+ if (valueType === '') {
313
+ el.value = val || ''
314
+ el.selected = val ? { key: val, text: val } : null
315
+ } else { // object
316
+ el.value = val.text || '' // key and text should be same
317
+ el.selected = val || null
318
+ }
319
+ }
320
+ } // onload end
321
+
322
+ el.onsearch = debounce(async (e) => {
323
+ // this.#xcols['state'].el.value // use this.#xcols to get latest values
324
+ // console.log(e.target.value, k, this.#record) // this.#record does not change until validated and submit
325
+ let parentVal = null
326
+ if (c?.options?.parentCol) {
327
+ const col = this.#xcols[c?.options?.parentCol]
328
+ if (col && col.el) parentVal = col.el.value
329
+ }
330
+ const res = await autocomplete(e.target.value, k, this.#record, parentVal)
331
+ el.items = res
332
+ }, 500)
333
+ el.onselect = (e) => { // onselect works (events handled by DOM), onselected need to use addEventListener
334
+ // TODO reset child value - may cascade down further
335
+ const childColName = c?.options?.childCol
336
+ if (childColName) {
337
+ const col = this.#xcols[childColName]
338
+ const childColObj = this.#config.cols[childColName]
339
+ if (col && col.el) {
340
+ if (childColObj?.ui?.attrs?.multiple) { // multiple
341
+ col.el.tags = []
342
+ } else {
343
+ col.el.value = ''
344
+ col.el.selected = null
345
+ }
346
+ }
347
+ }
348
+ console.log('t4t combobox onselect', e.detail)
349
+ }
350
+ } else { // input, textarea
351
+ if (this.mode === 'add') { // set the value
352
+ el.value = c.default || ''
353
+ } else if (this.mode === 'edit') {
354
+ // console.log('is FileList',this.#record[k] instanceof FileList, k, el.type === 'file')
355
+ el.value = el.type === 'file' ? '' : (this.#record[k] || '')
356
+ }
357
+ }
358
+ }
359
+ this.#xcols[k].el = el // set input element
360
+ }
361
+
362
+ if (errorLabel) {
363
+ this.#xcols[k].errorEl = el
364
+ }
365
+
366
+ if (className) el.className = className // set classes
367
+
368
+ // Bulma Specific Note (TODO tmprove this): if className has 'select' - it is bulma need to set is-multiple here if is multi select
369
+ if (node?.className && node.className.includes('select')) {
370
+ if (c?.ui?.attrs?.multiple) {
371
+ el.classList.add('is-multiple')
372
+ }
373
+ }
374
+
375
+ if (attrs) {
376
+ for (let key in attrs) {
377
+ el.setAttribute(key, attrs[key])
378
+ }
379
+ }
380
+ if (children) {
381
+ children.forEach(child => {
382
+ const childEl = this.formEl(child, k, c)
383
+ if (childEl) el.appendChild(childEl)
384
+ })
385
+ }
386
+ return el
387
+ }
388
+
389
+ _render() {
390
+ try {
391
+ // const el = this.querySelector('#form-wrapper')
392
+ const el = this.querySelector('.content-area')
393
+ if (!el) return // .content-area not found
394
+ el.innerHTML = ''
395
+ const { cols, auto, pk, required, multiKey } = this.#config
396
+ // console.log('this.#record', this.#record)
397
+ for (let col in cols) {
398
+ if (!auto.includes(col)) {
399
+ const c = cols[col]
400
+ // console.log('nonauto', c, this.mode)
401
+ if ((this.mode === 'add' && c.add !== 'hide') || (this.mode === 'edit' && c.edit !== 'hide')) {
402
+ const tagKey = c?.ui?.tag
403
+ if (tagKey) {
404
+ const fieldEl = this.formEl(framework[tagKey], col, c)
405
+
406
+ if (c?.ui?.attrs?.type === 'file' && this.mode === 'edit') { // field is file...
407
+ this.#xcols[col].errorEl.innerText = this.#record[col] || 'No Files Found'
408
+ // console.log(fieldEl, col)
409
+ }
410
+ el.appendChild(fieldEl)
411
+ }
412
+ }
413
+ } else {
414
+ // console.log('auto', col)
415
+ }
416
+ }
417
+
418
+ const btnSubmit = this.querySelector('.btn-submit')
419
+ // btnSubmit.classList.add('button')
420
+ btnSubmit.onclick = (e) => {
421
+ let error = false
422
+ // console.log('submit clicked')
423
+ e.preventDefault() // e.stopPropagation()
424
+
425
+ // check validity
426
+ for (let col in this.#xcols) {
427
+ if (this.#xcols[col].el) {
428
+ if (this.#xcols[col]?.el?.checkValidity) {
429
+ const valid = this.#xcols[col].el.checkValidity()
430
+ if (!valid) error = true
431
+ if (this.#xcols[col].errorEl) this.#xcols[col].errorEl.innerText = valid ? '' : this.#xcols[col].el.validationMessage
432
+ }
433
+ }
434
+ }
435
+
436
+ // console.log(this.#record)
437
+ if (!error) {
438
+ for (let col in this.#xcols) {
439
+ // console.log('this.#xcols', this.#xcols[col].el.tagName)
440
+ const inputEl = this.#xcols[col].el
441
+ if (inputEl) {
442
+ if (inputEl.tagName.toLowerCase() === 'select') {
443
+ // select options, [string] - done, [{ key, text }] - next
444
+ const selected = []
445
+ for (let opt of inputEl.selectedOptions) {
446
+ selected.push(opt.value)
447
+ }
448
+ this.#record[col] = selected.join(',')
449
+ } else if (inputEl.tagName.toLowerCase() === 'bwc-combobox') {
450
+ // TODO set the value
451
+ const c = this.#config.cols[col]
452
+ const val = c?.ui?.attrs?.multiple ? inputEl.tags : inputEl.selected
453
+ const valueType = c?.ui?.valueType
454
+
455
+ if (c?.ui?.attrs?.multiple) { // can be array in column or join table
456
+ if (valueType === '') {
457
+ this.#record[col] = val.map(item => item.text).join(',')
458
+ } else { // object
459
+ this.#record[col] = val
460
+ }
461
+ } else { // single
462
+ if (valueType === '') {
463
+ this.#record[col] = val.text
464
+ } else { // object
465
+ this.#record[col] = val
466
+ }
467
+ }
468
+ } else { // input, textarea
469
+ this.#record[col] = inputEl.value
470
+ if (inputEl.files) {
471
+ // console.log(inputEl.files instanceof FileList)
472
+ this.#record[col] = inputEl.files
473
+ }
474
+ }
475
+ }
476
+ }
477
+ // console.log('test submit', this.#record)
478
+ this.dispatchEvent(new CustomEvent('submit', { detail: { data: this.#record } }))
479
+ } else {
480
+ this.dispatchEvent(new CustomEvent('submit', { detail: { error } }))
481
+ }
482
+ }
483
+
484
+ const btnCancel = this.querySelector('.btn-cancel')
485
+ // btnCancel.classList.add('button')
486
+ btnCancel.onclick = (e) => {
487
+ e.preventDefault()
488
+ this.dispatchEvent(new CustomEvent('cancel'))
489
+ }
490
+ } catch (e) {
491
+ console.log('bwc-t4t-form', e)
492
+ }
493
+ }
494
+ }
495
+
496
+ customElements.define('bwc-t4t-form', BwcT4tForm) // or bwc-form-t4t
497
+
498
+ /*
499
+ <template v-if="tableCfg.cols[col].input === 'link'">
500
+ <mwc-textfield
501
+ @click="router.push('/' + tableCfg.cols[col].options.to + '?keyval=' + recordObj[showForm].key + '&keycol=' + tableCfg.cols[col].options.relatedCol)"
502
+ disabled
503
+ class="field-item"
504
+ :key="col + index"
505
+ :label="tableCfg.cols[col].label"
506
+ outlined
507
+ type="text"
508
+ v-model="recordObj[showForm][col]"
509
+ ></mwc-textfield>
510
+ </template>
511
+ */