@d-mok/quasar-app-extension-quasar-axe 2.1.6 → 2.1.8
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/package.json +2 -2
- package/src/templates/src/utils/index.ts +1 -3
- package/src/templates/src/utils/puppets/builder/index.ts +7 -7
- package/src/templates/src/utils/puppets/core/db.ts +51 -82
- package/src/templates/src/utils/puppets/core/index.ts +5 -8
- package/src/templates/src/utils/puppets/entity.ts +4 -10
- package/src/templates/src/utils/puppets/table.ts +11 -39
- package/src/templates/src/utils/puppets/type.ts +7 -5
- package/src/templates/src/utils/supabase.ts +117 -117
- package/src/templates/src/utils/supabase2.ts +105 -117
- package/src/templates/src/utils/settings.ts +0 -20
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@d-mok/quasar-app-extension-quasar-axe",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.8",
|
|
4
4
|
"description": "A Quasar App Extension",
|
|
5
5
|
"author": "d-mok <49301824+d-mok@users.noreply.github.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@handsontable/vue3": "^12.1.0",
|
|
18
|
-
"@supabase/supabase-js": "^
|
|
18
|
+
"@supabase/supabase-js": "^2.24.0",
|
|
19
19
|
"@types/lodash": "^4.14.182",
|
|
20
20
|
"@types/papaparse": "^5.3.2",
|
|
21
21
|
"@types/webpack-env": "^1.17.0",
|
|
@@ -3,9 +3,7 @@ import 'sapphire-js'
|
|
|
3
3
|
export { qDialog } from './dialog'
|
|
4
4
|
export * as qNotify from './notify'
|
|
5
5
|
|
|
6
|
-
export { HANDLE_ERROR, supabase } from './
|
|
7
|
-
|
|
8
|
-
export { localSettings } from './settings'
|
|
6
|
+
export { HANDLE_ERROR, supabase } from './supabase2'
|
|
9
7
|
|
|
10
8
|
export { TABLE, ENTITY } from './puppets'
|
|
11
9
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Core } from '../core'
|
|
2
|
-
import { Filter } from '../type'
|
|
2
|
+
import { Filter, Key } from '../type'
|
|
3
3
|
import { ui } from './ui'
|
|
4
4
|
|
|
5
5
|
type Promisable<T> = T | PromiseLike<T>
|
|
@@ -89,17 +89,17 @@ abstract class Builder<T, R> extends BuilderUI implements PromiseLike<void> {
|
|
|
89
89
|
abstract class FilterBuilder<T, R> extends Builder<T, R> {
|
|
90
90
|
protected filters: Filter<R>[] = []
|
|
91
91
|
|
|
92
|
-
eq<K extends
|
|
92
|
+
eq<K extends Key<R>>(key: K, val: R[K]): this {
|
|
93
93
|
this.filters.push({ type: 'eq', payload: { key, val } })
|
|
94
94
|
return this
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
in<K extends
|
|
97
|
+
in<K extends Key<R>>(key: K, vals: R[K][]): this {
|
|
98
98
|
this.filters.push({ type: 'in', payload: { key, vals } })
|
|
99
99
|
return this
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
like<K extends
|
|
102
|
+
like<K extends Key<R>>(key: K, pattern: string): this {
|
|
103
103
|
this.filters.push({ type: 'like', payload: { key, pattern } })
|
|
104
104
|
return this
|
|
105
105
|
}
|
|
@@ -109,7 +109,7 @@ abstract class FilterBuilder<T, R> extends Builder<T, R> {
|
|
|
109
109
|
return this
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
order<K extends
|
|
112
|
+
order<K extends Key<R>>(key: K, ascending: boolean = true): this {
|
|
113
113
|
this.filters.push({ type: 'order', payload: { key, asc: ascending } })
|
|
114
114
|
return this
|
|
115
115
|
}
|
|
@@ -123,7 +123,7 @@ export class SelectBuilder<T, R> extends FilterBuilder<T, R> {
|
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
export class SelectInBuilder<T, R, K extends
|
|
126
|
+
export class SelectInBuilder<T, R, K extends Key<R>> extends FilterBuilder<
|
|
127
127
|
T,
|
|
128
128
|
R
|
|
129
129
|
> {
|
|
@@ -213,7 +213,7 @@ export class UpsertBuilder<T, R> extends Builder<T, R> {
|
|
|
213
213
|
|
|
214
214
|
constructor(
|
|
215
215
|
private row: Partial<R>,
|
|
216
|
-
private conflictKeys:
|
|
216
|
+
private conflictKeys: Key<R>[],
|
|
217
217
|
core: Core<T, R>,
|
|
218
218
|
callback: (objs: T[]) => void
|
|
219
219
|
) {
|
|
@@ -3,59 +3,36 @@ import {
|
|
|
3
3
|
HANDLE_ERROR,
|
|
4
4
|
PostgrestFilterBuilder,
|
|
5
5
|
PostgrestSingleResponse,
|
|
6
|
-
} from '../../
|
|
6
|
+
} from '../../supabase2'
|
|
7
7
|
import { Filter } from '../type'
|
|
8
|
-
import _ from 'lodash'
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
type FilterBuilder = PostgrestFilterBuilder<any, any, any, any>
|
|
10
|
+
|
|
11
|
+
async function send(q: FilterBuilder): Promise<any[]> {
|
|
11
12
|
let { data, error } = await q
|
|
12
13
|
HANDLE_ERROR(data, error)
|
|
13
14
|
return data
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
async function sendSingle
|
|
17
|
-
q: PromiseLike<PostgrestSingleResponse<
|
|
18
|
-
): Promise<
|
|
17
|
+
async function sendSingle(
|
|
18
|
+
q: PromiseLike<PostgrestSingleResponse<any>>
|
|
19
|
+
): Promise<any[]> {
|
|
19
20
|
let { data, error } = await q
|
|
20
21
|
HANDLE_ERROR(data, error)
|
|
21
22
|
return [data]
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
function
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
q = q.like(
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function parseLimit<R>(
|
|
36
|
-
q: PostgrestFilterBuilder<R>,
|
|
37
|
-
filter: Filter<R>
|
|
38
|
-
): PostgrestFilterBuilder<R> {
|
|
39
|
-
if (filter.type === 'limit') q = q.limit(filter.payload)
|
|
40
|
-
return q
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function parseOrder<R>(
|
|
44
|
-
q: PostgrestFilterBuilder<R>,
|
|
45
|
-
filter: Filter<R>
|
|
46
|
-
): PostgrestFilterBuilder<R> {
|
|
47
|
-
if (filter.type === 'order')
|
|
48
|
-
q = q.order(filter.payload.key, { ascending: filter.payload.asc })
|
|
49
|
-
return q
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function parseFilters<R>(
|
|
53
|
-
q: PostgrestFilterBuilder<R>,
|
|
54
|
-
filters: Filter<R>[]
|
|
55
|
-
): PostgrestFilterBuilder<R> {
|
|
56
|
-
for (const filter of filters) q = parseFilter(q, filter)
|
|
57
|
-
for (const filter of filters) q = parseOrder(q, filter)
|
|
58
|
-
for (const filter of filters) q = parseLimit(q, filter)
|
|
25
|
+
function parseFilters(q: FilterBuilder, filters: Filter[]): FilterBuilder {
|
|
26
|
+
let sortedFilters = filters.sortedBy(f =>
|
|
27
|
+
['eq', 'in', 'like', 'limit', 'order'].indexOf(f.type)
|
|
28
|
+
)
|
|
29
|
+
for (const { type: t, payload: p } of sortedFilters) {
|
|
30
|
+
if (t === 'eq') q = q.eq(p.key, p.val)
|
|
31
|
+
if (t === 'in') q = q.in(p.key, p.vals)
|
|
32
|
+
if (t === 'like') q = q.like(p.key, p.pattern)
|
|
33
|
+
if (t === 'limit') q = q.limit(p)
|
|
34
|
+
if (t === 'order') q = q.order(p.key, { ascending: p.asc })
|
|
35
|
+
}
|
|
59
36
|
return q
|
|
60
37
|
}
|
|
61
38
|
|
|
@@ -64,89 +41,81 @@ function parseFilters<R>(
|
|
|
64
41
|
* Purely functional.
|
|
65
42
|
*/
|
|
66
43
|
class DB {
|
|
67
|
-
async select
|
|
44
|
+
async select(
|
|
68
45
|
table: string,
|
|
69
|
-
fields:
|
|
70
|
-
filters: Filter
|
|
71
|
-
): Promise<
|
|
72
|
-
let q = supabase.from
|
|
46
|
+
fields: string[],
|
|
47
|
+
filters: Filter[]
|
|
48
|
+
): Promise<any[]> {
|
|
49
|
+
let q = supabase.from(table).select(fields.join(','))
|
|
73
50
|
q = parseFilters(q, filters)
|
|
74
51
|
return send(q)
|
|
75
52
|
}
|
|
76
53
|
|
|
77
|
-
async selectIn
|
|
54
|
+
async selectIn(
|
|
78
55
|
table: string,
|
|
79
|
-
fields:
|
|
80
|
-
key:
|
|
81
|
-
vals:
|
|
82
|
-
filters: Filter
|
|
83
|
-
): Promise<
|
|
84
|
-
let getRows = (chunkPiece:
|
|
56
|
+
fields: string[],
|
|
57
|
+
key: string,
|
|
58
|
+
vals: any[],
|
|
59
|
+
filters: Filter[]
|
|
60
|
+
): Promise<any[]> {
|
|
61
|
+
let getRows = (chunkPiece: any[]): Promise<any[]> => {
|
|
85
62
|
let q = supabase
|
|
86
|
-
.from
|
|
63
|
+
.from(table)
|
|
87
64
|
.select(fields.join(','))
|
|
88
65
|
.in(key, chunkPiece)
|
|
89
66
|
q = parseFilters(q, filters)
|
|
90
67
|
return send(q)
|
|
91
68
|
}
|
|
92
|
-
let promises =
|
|
69
|
+
let promises = vals.chunk(100).map(getRows)
|
|
93
70
|
let result = await Promise.all(promises)
|
|
94
71
|
return result.flat()
|
|
95
72
|
}
|
|
96
73
|
|
|
97
|
-
async rpc<
|
|
98
|
-
fn
|
|
99
|
-
param: object,
|
|
100
|
-
filters: Filter<R>[]
|
|
101
|
-
): Promise<R[]> {
|
|
102
|
-
let q = supabase.rpc<R>(fn, param)
|
|
74
|
+
async rpc(fn: string, param: any, filters: Filter[]): Promise<any[]> {
|
|
75
|
+
let q = supabase.rpc(fn, param)
|
|
103
76
|
q = parseFilters(q, filters)
|
|
104
77
|
return send(q)
|
|
105
78
|
}
|
|
106
79
|
|
|
107
|
-
async insert
|
|
108
|
-
let q = supabase.from
|
|
80
|
+
async insert(table: string, rows: any[]): Promise<any[]> {
|
|
81
|
+
let q = supabase.from(table).insert(rows)
|
|
109
82
|
return send(q)
|
|
110
83
|
}
|
|
111
84
|
|
|
112
|
-
async update
|
|
85
|
+
async update(
|
|
113
86
|
table: string,
|
|
114
|
-
idKey:
|
|
115
|
-
idVal:
|
|
116
|
-
row:
|
|
117
|
-
): Promise<
|
|
118
|
-
let q = supabase.from
|
|
87
|
+
idKey: string,
|
|
88
|
+
idVal: any,
|
|
89
|
+
row: any
|
|
90
|
+
): Promise<any[]> {
|
|
91
|
+
let q = supabase.from(table).update(row).eq(idKey, idVal).single()
|
|
119
92
|
return sendSingle(q)
|
|
120
93
|
}
|
|
121
94
|
|
|
122
|
-
async delete
|
|
123
|
-
table
|
|
124
|
-
idKey: K,
|
|
125
|
-
idVal: R[K]
|
|
126
|
-
): Promise<R[]> {
|
|
127
|
-
let q = supabase.from<R>(table).delete().eq(idKey, idVal).single()
|
|
95
|
+
async delete(table: string, idKey: string, idVal: any): Promise<any[]> {
|
|
96
|
+
let q = supabase.from(table).delete().eq(idKey, idVal).single()
|
|
128
97
|
return sendSingle(q)
|
|
129
98
|
}
|
|
130
99
|
|
|
131
|
-
async upsert
|
|
100
|
+
async upsert(
|
|
132
101
|
table: string,
|
|
133
|
-
idKey:
|
|
134
|
-
row:
|
|
135
|
-
conflictKeys: (keyof
|
|
136
|
-
): Promise<
|
|
102
|
+
idKey: string,
|
|
103
|
+
row: any,
|
|
104
|
+
conflictKeys: (string & keyof typeof row)[]
|
|
105
|
+
): Promise<any[]> {
|
|
137
106
|
let conflict: any = {}
|
|
138
107
|
for (let f of conflictKeys) {
|
|
139
108
|
conflict[f] = row[f]
|
|
140
109
|
}
|
|
141
110
|
|
|
142
|
-
let q = supabase.from
|
|
111
|
+
let q = supabase.from(table).select('*').match(conflict)
|
|
143
112
|
let data = await send(q)
|
|
144
113
|
|
|
145
114
|
if (data.length > 1)
|
|
146
115
|
throw 'Fail to upsert! More than 1 rows matching the conflictKeys are found!'
|
|
147
116
|
|
|
148
117
|
if (data.length === 1) {
|
|
149
|
-
let id = data[0][idKey]
|
|
118
|
+
let id = data[0][idKey as keyof (typeof data)[0]]
|
|
150
119
|
return await this.update(table, idKey, id, row)
|
|
151
120
|
} else {
|
|
152
121
|
return await this.insert(table, [row])
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { Filter } from '../type'
|
|
1
|
+
import { Filter, Key } from '../type'
|
|
2
2
|
import { db } from './db'
|
|
3
3
|
|
|
4
4
|
/** I am responsible for transfering the state from Table to Builders.*/
|
|
5
5
|
export class Core<T, R> {
|
|
6
6
|
constructor(
|
|
7
7
|
public tableName: string,
|
|
8
|
-
public idKey:
|
|
9
|
-
public fields:
|
|
8
|
+
public idKey: Key<R>,
|
|
9
|
+
public fields: Key<R>[],
|
|
10
10
|
public convertor: ($: R) => T
|
|
11
11
|
) {}
|
|
12
12
|
|
|
@@ -14,7 +14,7 @@ export class Core<T, R> {
|
|
|
14
14
|
return db.select(this.tableName, this.fields, filters)
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
async fetchSelectIn<K extends
|
|
17
|
+
async fetchSelectIn<K extends Key<R>>(
|
|
18
18
|
key: K,
|
|
19
19
|
vals: R[K][],
|
|
20
20
|
filters: Filter<R>[]
|
|
@@ -38,10 +38,7 @@ export class Core<T, R> {
|
|
|
38
38
|
return db.delete(this.tableName, this.idKey, idVal)
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
async fetchUpsert(
|
|
42
|
-
row: Partial<R>,
|
|
43
|
-
conflictKeys: (keyof R)[]
|
|
44
|
-
): Promise<R[]> {
|
|
41
|
+
async fetchUpsert(row: Partial<R>, conflictKeys: Key<R>[]): Promise<R[]> {
|
|
45
42
|
return db.upsert(this.tableName, this.idKey, row, conflictKeys)
|
|
46
43
|
}
|
|
47
44
|
}
|
|
@@ -6,7 +6,7 @@ export function ENTITY<
|
|
|
6
6
|
K extends string & keyof R,
|
|
7
7
|
R extends InstanceType<TBase> = InstanceType<TBase>
|
|
8
8
|
>(RowClass: TBase, tableName: string, idKey: K) {
|
|
9
|
-
class
|
|
9
|
+
return class extends RowClass {
|
|
10
10
|
constructor(...args: any[]) {
|
|
11
11
|
super(...args)
|
|
12
12
|
let template = args[0] ?? {}
|
|
@@ -20,26 +20,20 @@ export function ENTITY<
|
|
|
20
20
|
static _idKey: K = idKey
|
|
21
21
|
|
|
22
22
|
static _fields: any[] = Object.keys(new RowClass())
|
|
23
|
-
}
|
|
24
23
|
|
|
25
|
-
class EDIT extends VIEW {
|
|
26
24
|
update(row: Partial<R>): UpdateBuilder<any, R> {
|
|
27
25
|
// @ts-ignore
|
|
28
|
-
return this._hostTable.
|
|
26
|
+
return this._hostTable.update(this[idKey], row)
|
|
29
27
|
}
|
|
30
28
|
|
|
31
29
|
delete(): DeleteBuilder<any, R> {
|
|
32
30
|
// @ts-ignore
|
|
33
|
-
return this._hostTable.
|
|
31
|
+
return this._hostTable.delete(this[idKey])
|
|
34
32
|
}
|
|
35
33
|
|
|
36
34
|
toggle(key: BooleanKeys<R>): UpdateBuilder<any, R> {
|
|
37
35
|
// @ts-ignore
|
|
38
|
-
return this._hostTable.
|
|
36
|
+
return this._hostTable.update(this[idKey], { [key]: !this[key] })
|
|
39
37
|
}
|
|
40
38
|
}
|
|
41
|
-
return {
|
|
42
|
-
EDIT,
|
|
43
|
-
VIEW,
|
|
44
|
-
}
|
|
45
39
|
}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
UpdateBuilder,
|
|
8
8
|
UpsertBuilder,
|
|
9
9
|
} from './builder'
|
|
10
|
-
import { Constructor } from './type'
|
|
10
|
+
import { Constructor, Key } from './type'
|
|
11
11
|
import { Core } from './core'
|
|
12
12
|
import { ENTITY } from './entity'
|
|
13
13
|
import { reactive } from 'vue'
|
|
@@ -58,7 +58,7 @@ export function TABLE<
|
|
|
58
58
|
return new Core<T, R>(tableName, idKey, fields, convertor(host))
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
class
|
|
61
|
+
return class extends Array<T> {
|
|
62
62
|
/** make reactive */
|
|
63
63
|
reactive(): this {
|
|
64
64
|
const rec = reactive(this) as this
|
|
@@ -71,9 +71,7 @@ export function TABLE<
|
|
|
71
71
|
|
|
72
72
|
protected onChange(): any {}
|
|
73
73
|
protected onEdit(): any {}
|
|
74
|
-
}
|
|
75
74
|
|
|
76
|
-
class TableView extends Base {
|
|
77
75
|
select() {
|
|
78
76
|
let callback = ($: T[]) => {
|
|
79
77
|
this.set($)
|
|
@@ -90,7 +88,7 @@ export function TABLE<
|
|
|
90
88
|
return new SelectBuilder(core(this), callback)
|
|
91
89
|
}
|
|
92
90
|
|
|
93
|
-
selectIn<K extends
|
|
91
|
+
selectIn<K extends Key<R>>(key: K, vals: R[K][]) {
|
|
94
92
|
let callback = ($: T[]) => {
|
|
95
93
|
this.set($)
|
|
96
94
|
this.sortBy($ => vals.indexOf($[key]))
|
|
@@ -99,7 +97,7 @@ export function TABLE<
|
|
|
99
97
|
return new SelectInBuilder(key, vals, core(this), callback)
|
|
100
98
|
}
|
|
101
99
|
|
|
102
|
-
patchIn<K extends
|
|
100
|
+
patchIn<K extends Key<R>>(key: K, vals: R[K][]) {
|
|
103
101
|
let callback = ($: T[]) => {
|
|
104
102
|
this.absorb($, idKey)
|
|
105
103
|
this.sortBy($ => vals.indexOf($[key]))
|
|
@@ -107,9 +105,7 @@ export function TABLE<
|
|
|
107
105
|
}
|
|
108
106
|
return new SelectInBuilder(key, vals, core(this), callback)
|
|
109
107
|
}
|
|
110
|
-
}
|
|
111
108
|
|
|
112
|
-
class TableRPC extends Base {
|
|
113
109
|
selectRPC(param: object = {}) {
|
|
114
110
|
let callback = ($: T[]) => {
|
|
115
111
|
this.set($)
|
|
@@ -125,9 +121,7 @@ export function TABLE<
|
|
|
125
121
|
}
|
|
126
122
|
return new SelectRPCBuilder(param, core(this), callback)
|
|
127
123
|
}
|
|
128
|
-
}
|
|
129
124
|
|
|
130
|
-
class TableEdit extends TableView {
|
|
131
125
|
insert(row: Partial<R>) {
|
|
132
126
|
let callback = ($: T[]) => {
|
|
133
127
|
this.push(...$)
|
|
@@ -150,11 +144,6 @@ export function TABLE<
|
|
|
150
144
|
)
|
|
151
145
|
}
|
|
152
146
|
|
|
153
|
-
updateRow(oldRow: R, updatedRow: Partial<R>) {
|
|
154
|
-
let idVal = oldRow[idKey]
|
|
155
|
-
return this.update(idVal, updatedRow)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
147
|
delete(idVal: R[ClassT['_idKey']]) {
|
|
159
148
|
let callback = ($: T[]) => {
|
|
160
149
|
_.remove(this, $ => $[idKey] === idVal)
|
|
@@ -166,12 +155,7 @@ export function TABLE<
|
|
|
166
155
|
)
|
|
167
156
|
}
|
|
168
157
|
|
|
169
|
-
|
|
170
|
-
let idVal = rowToDelete[idKey]
|
|
171
|
-
return this.delete(idVal)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
upsert(row: Partial<R>, conflictKeys: (keyof R)[]) {
|
|
158
|
+
upsert(row: Partial<R>, conflictKeys: Key<R>[]) {
|
|
175
159
|
let callback = ($: T[]) => {
|
|
176
160
|
this.absorb($, idKey)
|
|
177
161
|
this.onChange()
|
|
@@ -185,45 +169,33 @@ export function TABLE<
|
|
|
185
169
|
).notify('Updated!')
|
|
186
170
|
}
|
|
187
171
|
}
|
|
188
|
-
|
|
189
|
-
return {
|
|
190
|
-
EDIT: TableEdit,
|
|
191
|
-
VIEW: TableView,
|
|
192
|
-
RPC: TableRPC,
|
|
193
|
-
}
|
|
194
172
|
}
|
|
195
173
|
|
|
196
174
|
// // example
|
|
197
175
|
|
|
198
176
|
// class StudentRow {
|
|
199
|
-
|
|
200
|
-
// public
|
|
201
|
-
// public class: string = ""
|
|
177
|
+
// public name: string = ''
|
|
178
|
+
// public class: string = ''
|
|
202
179
|
// public num: number = 0
|
|
203
180
|
// public pass: boolean = false
|
|
204
181
|
// public pasds: boolean = false
|
|
205
|
-
|
|
206
182
|
// }
|
|
207
183
|
|
|
208
|
-
// class Student extends
|
|
209
|
-
|
|
184
|
+
// class Student extends ENTITY(StudentRow, 'student', 'num') {
|
|
210
185
|
// public full = this.class + this.name
|
|
211
|
-
|
|
212
186
|
// }
|
|
213
187
|
|
|
214
188
|
// let s = new Student()
|
|
215
189
|
// // s.ful
|
|
216
190
|
// s.toggle('pass')
|
|
217
191
|
|
|
218
|
-
// class StudentsCollection extends
|
|
219
|
-
|
|
192
|
+
// class StudentsCollection extends TABLE(Student) {
|
|
220
193
|
// protected override onChange() {
|
|
221
194
|
// return 1
|
|
222
195
|
// }
|
|
223
|
-
|
|
224
196
|
// }
|
|
225
197
|
|
|
226
198
|
// let ss = new StudentsCollection()
|
|
227
|
-
// ss.select().eq('
|
|
228
|
-
// ss.insert()
|
|
199
|
+
// ss.select().eq('name', 'a')
|
|
200
|
+
// ss.insert({ name: 'a' })
|
|
229
201
|
// ss.delete(1)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export type Filter<R> =
|
|
2
|
-
| { type: 'eq'; payload: { key:
|
|
3
|
-
| { type: 'in'; payload: { key:
|
|
4
|
-
| { type: 'like'; payload: { key:
|
|
1
|
+
export type Filter<R = any> =
|
|
2
|
+
| { type: 'eq'; payload: { key: Key<R>; val: any } }
|
|
3
|
+
| { type: 'in'; payload: { key: Key<R>; vals: any[] } }
|
|
4
|
+
| { type: 'like'; payload: { key: Key<R>; pattern: string } }
|
|
5
5
|
| { type: 'limit'; payload: number }
|
|
6
|
-
| { type: 'order'; payload: { key:
|
|
6
|
+
| { type: 'order'; payload: { key: Key<R>; asc: boolean } }
|
|
7
7
|
|
|
8
8
|
export type Constructor<T extends object = {}> = new (...args: any[]) => T
|
|
9
9
|
|
|
@@ -11,3 +11,5 @@ export type BooleanKeys<T> = {
|
|
|
11
11
|
[k in keyof T]: T[k] extends boolean ? k : never
|
|
12
12
|
}[keyof T]
|
|
13
13
|
// type OnlyBoolean<T> = { [k in BooleanKeys<T>]: boolean }
|
|
14
|
+
|
|
15
|
+
export type Key<R> = string & keyof R
|
|
@@ -1,130 +1,130 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} from '@supabase/supabase-js'
|
|
6
|
-
import { qDialog } from './dialog'
|
|
1
|
+
// import {
|
|
2
|
+
// createClient,
|
|
3
|
+
// PostgrestError,
|
|
4
|
+
// SupabaseClient,
|
|
5
|
+
// } from '@supabase/supabase-js'
|
|
6
|
+
// import { qDialog } from './dialog'
|
|
7
7
|
|
|
8
|
-
export type {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} from '@supabase/postgrest-js'
|
|
8
|
+
// export type {
|
|
9
|
+
// PostgrestFilterBuilder,
|
|
10
|
+
// PostgrestSingleResponse,
|
|
11
|
+
// } from '@supabase/postgrest-js'
|
|
12
12
|
|
|
13
|
-
declare module '@supabase/supabase-js' {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
13
|
+
// declare module '@supabase/supabase-js' {
|
|
14
|
+
// interface SupabaseClient {
|
|
15
|
+
// signIn: () => void
|
|
16
|
+
// accessTokenLife: () => number
|
|
17
|
+
// waitForSignin: () => Promise<void>
|
|
18
|
+
// email: () => string
|
|
19
|
+
// call: <T>(fn: string, params?: object | undefined) => Promise<T[]>
|
|
20
|
+
// callSingle: <T>(fn: string, params?: object | undefined) => Promise<T>
|
|
21
|
+
// }
|
|
22
|
+
// }
|
|
23
23
|
|
|
24
|
-
export let supabase: SupabaseClient
|
|
25
|
-
let SUPABASE_URL = process.env.SUPABASE_URL
|
|
26
|
-
let SUPABASE_KEY = process.env.SUPABASE_KEY
|
|
24
|
+
// export let supabase: SupabaseClient
|
|
25
|
+
// let SUPABASE_URL = process.env.SUPABASE_URL
|
|
26
|
+
// let SUPABASE_KEY = process.env.SUPABASE_KEY
|
|
27
27
|
|
|
28
|
-
if (SUPABASE_URL && SUPABASE_KEY) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
// if (SUPABASE_URL && SUPABASE_KEY) {
|
|
29
|
+
// supabase = createClient(SUPABASE_URL, SUPABASE_KEY, {
|
|
30
|
+
// autoRefreshToken: true,
|
|
31
|
+
// })
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
// supabase.signIn = async () => {
|
|
34
|
+
// await supabase.auth.signOut()
|
|
35
|
+
// await supabase.auth.signIn(
|
|
36
|
+
// { provider: 'google' },
|
|
37
|
+
// { redirectTo: window.location.href }
|
|
38
|
+
// )
|
|
39
|
+
// }
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
// supabase.accessTokenLife = () => {
|
|
42
|
+
// const session = supabase.auth.session()
|
|
43
|
+
// if (session?.expires_at === undefined) return -999999
|
|
44
|
+
// const expireAt = new Date(session.expires_at * 1000)
|
|
45
|
+
// const ms = expireAt.getTime() - new Date().getTime() // ms
|
|
46
|
+
// return Math.round(ms / 1000)
|
|
47
|
+
// }
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
// supabase.auth.onAuthStateChange((event, session) => {
|
|
50
|
+
// console.log('[SUPABASE AUTH]', event, session?.user?.email)
|
|
51
|
+
// })
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
53
|
+
// supabase.waitForSignin = async function (): Promise<void> {
|
|
54
|
+
// setInterval(() => {
|
|
55
|
+
// if (supabase.auth.session() === null) supabase.signIn()
|
|
56
|
+
// }, 1000)
|
|
57
|
+
// await waitFor(
|
|
58
|
+
// () => supabase.auth.session() !== null,
|
|
59
|
+
// '[Wait for SignIn] waiting...',
|
|
60
|
+
// '[Wait for SignIn] DONE!'
|
|
61
|
+
// )
|
|
62
|
+
// }
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
// supabase.call = async function <T>(
|
|
65
|
+
// fn: string,
|
|
66
|
+
// params?: object | undefined
|
|
67
|
+
// ): Promise<T[]> {
|
|
68
|
+
// let { data, error } = await supabase.rpc<T>(fn, params)
|
|
69
|
+
// HANDLE_ERROR(data, error)
|
|
70
|
+
// return data
|
|
71
|
+
// }
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
73
|
+
// supabase.callSingle = async function <T>(
|
|
74
|
+
// fn: string,
|
|
75
|
+
// params?: object | undefined
|
|
76
|
+
// ): Promise<T> {
|
|
77
|
+
// let { data, error } = await supabase.rpc<T>(fn, params).single()
|
|
78
|
+
// HANDLE_ERROR(data, error)
|
|
79
|
+
// return data
|
|
80
|
+
// }
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
82
|
+
// supabase.email = function (): string {
|
|
83
|
+
// return supabase.auth.user()?.email ?? 'unauthenticated'
|
|
84
|
+
// }
|
|
85
|
+
// }
|
|
86
86
|
|
|
87
|
-
export function HANDLE_ERROR<T>(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
): asserts data {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
87
|
+
// export function HANDLE_ERROR<T>(
|
|
88
|
+
// data: T[] | T | null,
|
|
89
|
+
// error: PostgrestError | null
|
|
90
|
+
// ): asserts data {
|
|
91
|
+
// if (error) {
|
|
92
|
+
// if (error.message === 'JWSError JWSInvalidSignature') {
|
|
93
|
+
// localStorage.removeItem('supabase.auth.token')
|
|
94
|
+
// qDialog.error(
|
|
95
|
+
// 'Database Error!',
|
|
96
|
+
// 'Your login session has expired. Please try refreshing the website.'
|
|
97
|
+
// )
|
|
98
|
+
// throw error
|
|
99
|
+
// }
|
|
100
|
+
// let msg = ''
|
|
101
|
+
// msg += error.details ?? '' + '<br/>'
|
|
102
|
+
// msg += error.message ?? '' + '<br/>'
|
|
103
|
+
// msg += error.hint ?? ''
|
|
104
|
+
// qDialog.error('Database Error!', msg)
|
|
105
|
+
// throw error
|
|
106
|
+
// }
|
|
107
|
+
// if (data === null) {
|
|
108
|
+
// qDialog.error('Supabase Error!', 'Returned data is null')
|
|
109
|
+
// throw 'supabase return data is null!'
|
|
110
|
+
// }
|
|
111
|
+
// }
|
|
112
112
|
|
|
113
|
-
async function waitFor(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
113
|
+
// async function waitFor(
|
|
114
|
+
// predicate: () => boolean,
|
|
115
|
+
// waitingMsg: string,
|
|
116
|
+
// doneMsg: string
|
|
117
|
+
// ) {
|
|
118
|
+
// return new Promise(resolve => {
|
|
119
|
+
// function checker() {
|
|
120
|
+
// if (predicate()) {
|
|
121
|
+
// console.log(doneMsg)
|
|
122
|
+
// resolve(true)
|
|
123
|
+
// } else {
|
|
124
|
+
// console.log(waitingMsg)
|
|
125
|
+
// setTimeout(() => checker(), 50)
|
|
126
|
+
// }
|
|
127
|
+
// }
|
|
128
|
+
// checker()
|
|
129
|
+
// })
|
|
130
|
+
// }
|
|
@@ -1,128 +1,116 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import {
|
|
2
|
+
createClient,
|
|
3
|
+
PostgrestError,
|
|
4
|
+
SupabaseClient,
|
|
5
|
+
Session,
|
|
6
|
+
SupabaseClientOptions,
|
|
7
|
+
} from '@supabase/supabase-js'
|
|
8
|
+
import { qDialog } from './dialog'
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
export type {
|
|
11
|
+
PostgrestFilterBuilder,
|
|
12
|
+
PostgrestSingleResponse,
|
|
13
|
+
} from '@supabase/postgrest-js'
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
let SUPABASE_URL = process.env.SUPABASE_URL ?? ''
|
|
16
|
+
let SUPABASE_KEY = process.env.SUPABASE_KEY ?? ''
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
export class MySupabaseClient extends SupabaseClient {
|
|
19
|
+
email: string = 'unauthenticated'
|
|
20
|
+
session: Session | null = null
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
22
|
+
constructor() {
|
|
23
|
+
super(SUPABASE_URL, SUPABASE_KEY, {
|
|
24
|
+
auth: {
|
|
25
|
+
autoRefreshToken: true,
|
|
26
|
+
persistSession: true,
|
|
27
|
+
detectSessionInUrl: true,
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
this.auth.onAuthStateChange((event, session) => {
|
|
31
|
+
console.log('[SUPABASE AUTH]', event, session?.user?.email)
|
|
32
|
+
this.session = session
|
|
33
|
+
this.email = session?.user?.email ?? 'unauthenticated'
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
async signIn() {
|
|
38
|
+
await this.auth.signOut()
|
|
39
|
+
await this.auth.signInWithOAuth({
|
|
40
|
+
provider: 'google',
|
|
41
|
+
options: {
|
|
42
|
+
redirectTo: window.location.href,
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
async call<T>(fn: string, params?: object | undefined): Promise<T[]> {
|
|
48
|
+
let { data, error } = await this.rpc<string, any>(fn, params)
|
|
49
|
+
HANDLE_ERROR(data, error)
|
|
50
|
+
return data
|
|
51
|
+
}
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
async callSingle<T>(fn: string, params?: object | undefined): Promise<T> {
|
|
54
|
+
let { data, error } = await this.rpc<string, any>(fn, params).single()
|
|
55
|
+
HANDLE_ERROR(data, error)
|
|
56
|
+
return data as T
|
|
57
|
+
}
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
59
|
+
async waitForSignin(): Promise<void> {
|
|
60
|
+
setInterval(() => {
|
|
61
|
+
if (this.session === null) this.signIn()
|
|
62
|
+
}, 1000)
|
|
63
|
+
waitFor(
|
|
64
|
+
() => this.session !== null,
|
|
65
|
+
'[Wait for SignIn] waiting...',
|
|
66
|
+
'[Wait for SignIn] DONE!'
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
// // let SUPABASE_URL = process.env.SUPABASE_URL
|
|
73
|
-
// // let SUPABASE_KEY = process.env.SUPABASE_KEY
|
|
71
|
+
export let supabase = new MySupabaseClient()
|
|
74
72
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
73
|
+
export function HANDLE_ERROR<T>(
|
|
74
|
+
data: T[] | T | null,
|
|
75
|
+
error: PostgrestError | null
|
|
76
|
+
): asserts data {
|
|
77
|
+
if (error) {
|
|
78
|
+
if (error.message === 'JWSError JWSInvalidSignature') {
|
|
79
|
+
localStorage.removeItem('supabase.auth.token')
|
|
80
|
+
qDialog.error(
|
|
81
|
+
'Database Error!',
|
|
82
|
+
'Your login session has expired. Please try refreshing the website.'
|
|
83
|
+
)
|
|
84
|
+
throw error
|
|
85
|
+
}
|
|
86
|
+
let msg = ''
|
|
87
|
+
msg += error.details ?? '' + '<br/>'
|
|
88
|
+
msg += error.message ?? '' + '<br/>'
|
|
89
|
+
msg += error.hint ?? ''
|
|
90
|
+
qDialog.error('Database Error!', msg)
|
|
91
|
+
throw error
|
|
92
|
+
}
|
|
93
|
+
if (data === null) {
|
|
94
|
+
qDialog.error('Supabase Error!', 'Returned data is null')
|
|
95
|
+
throw 'supabase return data is null!'
|
|
96
|
+
}
|
|
97
|
+
}
|
|
84
98
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
// throw error
|
|
104
|
-
// }
|
|
105
|
-
// if (data === null) {
|
|
106
|
-
// qDialog.error('Supabase Error!', 'Returned data is null')
|
|
107
|
-
// throw 'supabase return data is null!'
|
|
108
|
-
// }
|
|
109
|
-
// }
|
|
110
|
-
|
|
111
|
-
// async function waitFor(
|
|
112
|
-
// predicate: () => boolean,
|
|
113
|
-
// waitingMsg: string,
|
|
114
|
-
// doneMsg: string
|
|
115
|
-
// ) {
|
|
116
|
-
// return new Promise(resolve => {
|
|
117
|
-
// async function checker() {
|
|
118
|
-
// if (predicate()) {
|
|
119
|
-
// console.log(doneMsg)
|
|
120
|
-
// resolve(true)
|
|
121
|
-
// } else {
|
|
122
|
-
// console.log(waitingMsg)
|
|
123
|
-
// setTimeout(checker, 50)
|
|
124
|
-
// }
|
|
125
|
-
// }
|
|
126
|
-
// checker()
|
|
127
|
-
// })
|
|
128
|
-
// }
|
|
99
|
+
async function waitFor(
|
|
100
|
+
predicate: () => boolean,
|
|
101
|
+
waitingMsg: string,
|
|
102
|
+
doneMsg: string
|
|
103
|
+
) {
|
|
104
|
+
return new Promise(resolve => {
|
|
105
|
+
async function checker() {
|
|
106
|
+
if (predicate()) {
|
|
107
|
+
console.log(doneMsg)
|
|
108
|
+
resolve(true)
|
|
109
|
+
} else {
|
|
110
|
+
console.log(waitingMsg)
|
|
111
|
+
setTimeout(checker, 50)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
checker()
|
|
115
|
+
})
|
|
116
|
+
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { reactive, effect } from '@vue/reactivity'
|
|
2
|
-
|
|
3
|
-
function getFromLocal<T extends object>(key: string): T {
|
|
4
|
-
let val = localStorage.getItem(key)
|
|
5
|
-
return val === null ? {} : JSON.parse(val)
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function localSettings<T extends object>(
|
|
9
|
-
preset: T,
|
|
10
|
-
localStorageKey = 'settings'
|
|
11
|
-
): T {
|
|
12
|
-
let ss = reactive({ ...preset, ...getFromLocal<T>(localStorageKey) })
|
|
13
|
-
effect(() => {
|
|
14
|
-
localStorage.setItem('settings', JSON.stringify(ss))
|
|
15
|
-
for (let k in ss) {
|
|
16
|
-
let val = ss[k]
|
|
17
|
-
}
|
|
18
|
-
})
|
|
19
|
-
return ss as T
|
|
20
|
-
}
|