@d-mok/quasar-app-extension-quasar-axe 1.0.3 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/boot/RegComp.ts +1 -0
- package/src/utils/ORM/basic.ts +82 -96
- package/src/utils/ORM/table.ts +63 -3
- package/src/utils/ORM/view.ts +23 -44
- package/src/utils/supabase.ts +1 -1
package/package.json
CHANGED
package/src/boot/RegComp.ts
CHANGED
package/src/utils/ORM/basic.ts
CHANGED
|
@@ -12,29 +12,20 @@ export type Where<T> =
|
|
|
12
12
|
| T[K][]
|
|
13
13
|
| { like: string }
|
|
14
14
|
}
|
|
15
|
-
| ((_: PostgrestFilterBuilder) => any)
|
|
16
15
|
|
|
17
|
-
// export type OrderBy<T> =
|
|
18
|
-
// strKeyOf<T>
|
|
19
|
-
// | { [K in keyof T]?: true | false }
|
|
20
16
|
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
type Action = 'select' | 'insert' | 'update' | 'delete' | 'patch'
|
|
24
|
-
|
|
18
|
+
type Action = 'select' | 'insert' | 'update' | 'delete'
|
|
25
19
|
|
|
26
20
|
|
|
27
21
|
function loadingBar(on: boolean): void {
|
|
28
|
-
on
|
|
22
|
+
on
|
|
23
|
+
? setTimeout(() => LoadingBar.setDefaults({ size: "5px" }), 3000)
|
|
29
24
|
: LoadingBar.setDefaults({ size: "0px" })
|
|
30
25
|
}
|
|
31
26
|
|
|
32
27
|
|
|
33
28
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
29
|
/**
|
|
39
30
|
* A subclass of array designed as an ORM.
|
|
40
31
|
* @template T - type of element class
|
|
@@ -42,28 +33,38 @@ function loadingBar(on: boolean): void {
|
|
|
42
33
|
*/
|
|
43
34
|
export abstract class BasicTable<T extends R, R extends object> extends Sheet<T>{
|
|
44
35
|
|
|
36
|
+
constructor(...args: any) {
|
|
37
|
+
super(args)
|
|
38
|
+
if (this.caching) {
|
|
39
|
+
this.restoreCache()
|
|
40
|
+
setInterval(() => this.screenCache(), 6000 + Math.random() * 3000)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
45
44
|
/**
|
|
46
45
|
* The name of the database table.
|
|
47
46
|
*/
|
|
48
47
|
public abstract readonly tableName: string
|
|
48
|
+
|
|
49
49
|
/**
|
|
50
50
|
* The name of the ID field.
|
|
51
51
|
*/
|
|
52
52
|
public abstract readonly idField: strKeyOf<R>
|
|
53
|
+
|
|
53
54
|
/**
|
|
54
55
|
* The class of the elements.
|
|
55
56
|
*/
|
|
56
57
|
public abstract readonly entityClass: new (_: Partial<R>) => T
|
|
58
|
+
|
|
57
59
|
/**
|
|
58
60
|
* The default ordering instruction object.
|
|
59
61
|
*/
|
|
60
62
|
public readonly ordering: Readonly<Ordering<T>> = []
|
|
61
|
-
// /**
|
|
62
|
-
// * The default fields to select
|
|
63
|
-
// */
|
|
64
|
-
// public readonly selectFields: strKeyOf<R>[] = []
|
|
65
|
-
|
|
66
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Auto Cache
|
|
66
|
+
*/
|
|
67
|
+
public readonly caching: boolean = false
|
|
67
68
|
|
|
68
69
|
private convert(data: R[]): T[] {
|
|
69
70
|
return data.map($ => new this.entityClass($))
|
|
@@ -75,41 +76,21 @@ export abstract class BasicTable<T extends R, R extends object> extends Sheet<T>
|
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
// private defaultOrder(): void {
|
|
80
|
-
// if (this.ordering !== undefined)
|
|
81
|
-
// this.order(...this.ordering)
|
|
82
|
-
// }
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
// private selectedFields() {
|
|
86
|
-
// return this.selectFields === undefined ? "*" : this.selectFields.join(",")
|
|
87
|
-
// }
|
|
88
|
-
|
|
89
|
-
|
|
90
79
|
protected idWhere(id: R[this['idField']]): any {
|
|
91
80
|
return { [this.idField]: id }
|
|
92
81
|
}
|
|
93
82
|
|
|
94
83
|
|
|
95
|
-
protected idsWhere(ids: R[this['idField']][]): any {
|
|
96
|
-
return { [this.idField]: ids }
|
|
97
|
-
}
|
|
98
84
|
|
|
99
85
|
|
|
100
|
-
protected orderById(ids: R[this['idField']][]): void {
|
|
101
|
-
let ord = { [this.idField]: ids } as any
|
|
102
|
-
this.order(ord)
|
|
103
|
-
}
|
|
104
86
|
|
|
105
87
|
|
|
106
|
-
|
|
88
|
+
|
|
89
|
+
private APIAction(type: Action, content: Partial<R>[]) {
|
|
107
90
|
let q = supabase.from<R>(this.tableName)
|
|
108
91
|
switch (type) {
|
|
109
92
|
case 'select':
|
|
110
93
|
return q.select('*')
|
|
111
|
-
case 'patch':
|
|
112
|
-
return q.select('*')
|
|
113
94
|
case 'delete':
|
|
114
95
|
return q.delete()
|
|
115
96
|
case 'insert':
|
|
@@ -121,21 +102,19 @@ export abstract class BasicTable<T extends R, R extends object> extends Sheet<T>
|
|
|
121
102
|
|
|
122
103
|
protected async API({
|
|
123
104
|
type,
|
|
124
|
-
content,
|
|
125
|
-
where,
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
slience
|
|
105
|
+
content = [],
|
|
106
|
+
where = {},
|
|
107
|
+
custom = $ => $,
|
|
108
|
+
action = ($: any) => { },
|
|
109
|
+
confirm = undefined,
|
|
110
|
+
notify = undefined,
|
|
111
|
+
slience = false
|
|
132
112
|
}: {
|
|
133
113
|
type: Action
|
|
134
114
|
content?: Partial<R>[]
|
|
135
115
|
where?: Where<R>
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
action: (entities: T[]) => void
|
|
116
|
+
custom?: (_: PostgrestFilterBuilder) => PostgrestFilterBuilder
|
|
117
|
+
action?: (entities: T[]) => void
|
|
139
118
|
confirm?: [string, string?]
|
|
140
119
|
notify?: string
|
|
141
120
|
slience?: boolean
|
|
@@ -145,12 +124,12 @@ export abstract class BasicTable<T extends R, R extends object> extends Sheet<T>
|
|
|
145
124
|
|
|
146
125
|
let q = this.APIAction(type, content)
|
|
147
126
|
q = applyWhere(q, where)
|
|
148
|
-
|
|
149
|
-
// q = applyLimit(q, limit)
|
|
127
|
+
q = custom(q)
|
|
150
128
|
|
|
151
129
|
let { data, error } = await q
|
|
152
130
|
handleError(data, error)
|
|
153
131
|
|
|
132
|
+
if (this.caching) this.pushCache(data)
|
|
154
133
|
let entities = this.convert(data)
|
|
155
134
|
action(entities)
|
|
156
135
|
this.order(...this.ordering)
|
|
@@ -162,8 +141,6 @@ export abstract class BasicTable<T extends R, R extends object> extends Sheet<T>
|
|
|
162
141
|
|
|
163
142
|
|
|
164
143
|
|
|
165
|
-
|
|
166
|
-
|
|
167
144
|
/**
|
|
168
145
|
* Get an item by `criteria`. If not found, return a new object.
|
|
169
146
|
*/
|
|
@@ -172,66 +149,75 @@ export abstract class BasicTable<T extends R, R extends object> extends Sheet<T>
|
|
|
172
149
|
}
|
|
173
150
|
|
|
174
151
|
|
|
175
|
-
|
|
176
|
-
|
|
152
|
+
/**
|
|
153
|
+
* Get a reactive copy of this.
|
|
154
|
+
*/
|
|
155
|
+
reactive(): this {
|
|
156
|
+
return reactive(this) as this
|
|
177
157
|
}
|
|
178
158
|
|
|
179
159
|
|
|
180
160
|
|
|
181
|
-
|
|
161
|
+
private get cache(): R[] {
|
|
162
|
+
return JSON.parse(localStorage.getItem(this.tableName) ?? '[]')
|
|
163
|
+
}
|
|
182
164
|
|
|
165
|
+
private set cache(data: R[]) {
|
|
166
|
+
let json = JSON.stringify(data)
|
|
167
|
+
localStorage.setItem(this.tableName, json)
|
|
168
|
+
}
|
|
183
169
|
|
|
170
|
+
private restoreCache() {
|
|
171
|
+
let entities = this.convert(this.cache)
|
|
172
|
+
this.set(entities)
|
|
173
|
+
if (process.env.DEBUGGING)
|
|
174
|
+
console.log(`[${this.tableName}] restore cache`)
|
|
175
|
+
}
|
|
184
176
|
|
|
177
|
+
private pushCache(data: R[]) {
|
|
178
|
+
this.cache = [...this.cache, ...data]
|
|
179
|
+
if (process.env.DEBUGGING)
|
|
180
|
+
console.log(`[${this.tableName}] push cache`)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private screenCache() {
|
|
184
|
+
let ids: any[] = this.scan(this.idField)
|
|
185
|
+
this.cache = this.cache.filter($ => ids.includes($[this.idField]))
|
|
186
|
+
if (process.env.DEBUGGING)
|
|
187
|
+
console.log(`[${this.tableName}] screen cache`)
|
|
188
|
+
}
|
|
185
189
|
|
|
186
190
|
|
|
187
191
|
|
|
188
|
-
function applyWhere(q: PostgrestFilterBuilder, where?: Where<any>): PostgrestFilterBuilder {
|
|
189
|
-
if (where === undefined)
|
|
190
|
-
return q
|
|
191
192
|
|
|
192
|
-
if (typeof where === 'function')
|
|
193
|
-
return where(q)
|
|
194
193
|
|
|
195
|
-
for (let key in where) {
|
|
196
|
-
let val = where[key]
|
|
197
|
-
if (Array.isArray(val)) {
|
|
198
|
-
// treat as in
|
|
199
|
-
q = q.in(key, val)
|
|
200
|
-
} else if (typeof val === 'object') {
|
|
201
|
-
if ('like' in val) {
|
|
202
|
-
// treat as like
|
|
203
|
-
let { like } = val as { like: string }
|
|
204
|
-
q = q.like(key, like)
|
|
205
|
-
}
|
|
206
|
-
} else {
|
|
207
|
-
// treat as eq
|
|
208
|
-
q = q.eq(key, val)
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
return q
|
|
212
194
|
}
|
|
213
195
|
|
|
214
|
-
// function applyOrderBy(q: PostgrestFilterBuilder, orderBy?: OrderBy<any>): PostgrestFilterBuilder {
|
|
215
|
-
// if (orderBy === undefined)
|
|
216
|
-
// return q
|
|
217
196
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
// let field = Object.keys(orderBy)[0]
|
|
222
|
-
// let ascending = orderBy[field]
|
|
223
|
-
// q = q.order(field, { ascending })
|
|
224
|
-
// }
|
|
225
|
-
// return q
|
|
226
|
-
// }
|
|
197
|
+
function whereType(val: any): string {
|
|
198
|
+
if (Array.isArray(val))
|
|
199
|
+
return 'in'
|
|
227
200
|
|
|
201
|
+
if (typeof val === 'object' && 'like' in val)
|
|
202
|
+
return 'like'
|
|
228
203
|
|
|
204
|
+
return 'eq'
|
|
205
|
+
}
|
|
229
206
|
|
|
230
|
-
// function applyLimit(q: PostgrestFilterBuilder, limit?: number): PostgrestFilterBuilder {
|
|
231
|
-
// if (limit === undefined)
|
|
232
|
-
// return q
|
|
233
207
|
|
|
234
|
-
// return q.limit(limit)
|
|
235
|
-
// }
|
|
236
208
|
|
|
209
|
+
function applyWhere(q: PostgrestFilterBuilder, where: Where<any>): PostgrestFilterBuilder {
|
|
210
|
+
for (let key in where) {
|
|
211
|
+
let val = where[key]
|
|
212
|
+
let type = whereType(val)
|
|
213
|
+
|
|
214
|
+
if (type === 'in')
|
|
215
|
+
q = q.in(key, val)
|
|
216
|
+
if (type === 'like')
|
|
217
|
+
q = q.like(key, val.like)
|
|
218
|
+
if (type === 'eq')
|
|
219
|
+
q = q.eq(key, val)
|
|
220
|
+
}
|
|
221
|
+
return q
|
|
222
|
+
}
|
|
237
223
|
|
package/src/utils/ORM/table.ts
CHANGED
|
@@ -28,7 +28,7 @@ export abstract class Table<T extends R, R extends object> extends TableView<T,
|
|
|
28
28
|
await this.API({
|
|
29
29
|
type: 'insert',
|
|
30
30
|
content: Array.isArray(rows) ? rows : [rows],
|
|
31
|
-
action:
|
|
31
|
+
action: $ => this.push(...$),
|
|
32
32
|
confirm,
|
|
33
33
|
notify,
|
|
34
34
|
slience
|
|
@@ -45,7 +45,7 @@ export abstract class Table<T extends R, R extends object> extends TableView<T,
|
|
|
45
45
|
type: 'update',
|
|
46
46
|
content: [row],
|
|
47
47
|
where: this.idWhere(id),
|
|
48
|
-
action:
|
|
48
|
+
action: $ => this.merge($, this.idField),
|
|
49
49
|
confirm,
|
|
50
50
|
notify,
|
|
51
51
|
slience
|
|
@@ -88,7 +88,7 @@ export abstract class Table<T extends R, R extends object> extends TableView<T,
|
|
|
88
88
|
await this.API({
|
|
89
89
|
type: 'delete',
|
|
90
90
|
where: this.idWhere(id),
|
|
91
|
-
action:
|
|
91
|
+
action: $ => this.discard(this.idWhere(id) as Criteria<T>),
|
|
92
92
|
confirm,
|
|
93
93
|
notify,
|
|
94
94
|
slience
|
|
@@ -151,3 +151,63 @@ export function Empower<RCls extends Constructor, R = InstanceType<RCls>>(BaseCl
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
// class FolderRow {
|
|
167
|
+
// id: number = 0
|
|
168
|
+
// subject: string = ""
|
|
169
|
+
// category: string = ""
|
|
170
|
+
// teacher: string = ""
|
|
171
|
+
// bank: string = ""
|
|
172
|
+
// name: string = ""
|
|
173
|
+
// pub_q: boolean = false
|
|
174
|
+
// pub_s: boolean = false
|
|
175
|
+
// codes: string[] = []
|
|
176
|
+
// modified: number = 0
|
|
177
|
+
|
|
178
|
+
// constructor(row = {}) {
|
|
179
|
+
// Object.assign(this, row)
|
|
180
|
+
// }
|
|
181
|
+
// }
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
// export class Folder extends FolderRow {
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
// }
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
// export class FolderTable extends Table<Folder, FolderRow>{
|
|
196
|
+
// readonly tableName = 'folder'
|
|
197
|
+
// readonly idField = 'id' as const
|
|
198
|
+
// readonly entityClass = Folder
|
|
199
|
+
// readonly ordering = [
|
|
200
|
+
// 'subject',
|
|
201
|
+
// { category: ['topic', 'paper', 'exercise'] },
|
|
202
|
+
// 'bank',
|
|
203
|
+
// 'name'
|
|
204
|
+
// ] as const
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
// }
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
// export const Folders = (new FolderTable).reactive()
|
|
212
|
+
|
|
213
|
+
// Folders.selectIn('teacher', [1,2,3])
|
package/src/utils/ORM/view.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { BasicTable, Where } from './basic'
|
|
2
|
+
import { PostgrestFilterBuilder } from '../supabase'
|
|
3
|
+
import { strKeyOf } from 'sapphire-js'
|
|
2
4
|
|
|
3
5
|
|
|
4
6
|
type selectOptions<R> = {
|
|
5
7
|
where?: Where<R>
|
|
6
|
-
|
|
7
|
-
// limit?: number
|
|
8
|
+
custom?: (_: PostgrestFilterBuilder) => PostgrestFilterBuilder
|
|
8
9
|
confirm?: [string, string?]
|
|
9
10
|
notify?: string
|
|
10
11
|
slience?: boolean
|
|
12
|
+
patch?: boolean
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
|
|
@@ -30,65 +32,42 @@ function chunk<T>(arr: T[], size: number): T[][] {
|
|
|
30
32
|
*/
|
|
31
33
|
export abstract class TableView<T extends R, R extends object> extends BasicTable<T, R>{
|
|
32
34
|
|
|
33
|
-
async select({ where, confirm, notify, slience }: selectOptions<R> = {}) {
|
|
35
|
+
async select({ where, custom, confirm, notify, slience, patch }: selectOptions<R> = {}) {
|
|
36
|
+
if (!patch) this.clear()
|
|
34
37
|
await this.API({
|
|
35
38
|
type: 'select',
|
|
36
39
|
where,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
action: entities => this.set(entities),
|
|
40
|
-
// order: true,
|
|
40
|
+
custom,
|
|
41
|
+
action: $ => patch ? this.absorb($, this.idField) : this.set($),
|
|
41
42
|
confirm,
|
|
42
43
|
notify,
|
|
43
44
|
slience
|
|
44
45
|
})
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
async patch({ where, confirm, notify, slience }: selectOptions<R> = {}) {
|
|
48
|
-
await this.API({
|
|
49
|
-
type: 'patch',
|
|
50
|
-
where,
|
|
51
|
-
// orderBy,
|
|
52
|
-
// limit,
|
|
53
|
-
action: entities => this.absorb(entities, this.idField),
|
|
54
|
-
confirm,
|
|
55
|
-
notify,
|
|
56
|
-
slience
|
|
57
|
-
})
|
|
58
|
-
}
|
|
59
48
|
|
|
60
49
|
|
|
61
|
-
async
|
|
62
|
-
|
|
63
|
-
|
|
50
|
+
async selectIn<F extends strKeyOf<R>>(
|
|
51
|
+
field: F,
|
|
52
|
+
values: R[F][],
|
|
53
|
+
{ where, custom, confirm, notify, slience, patch }: selectOptions<R> = {},
|
|
64
54
|
chunkSize = 100,
|
|
65
55
|
) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
56
|
+
if (!patch) this.clear()
|
|
57
|
+
let getSelect = (ck: R[F][]): Promise<void> => this.select({
|
|
58
|
+
where: { [field]: ck, ...where },
|
|
59
|
+
custom,
|
|
60
|
+
confirm,
|
|
61
|
+
notify,
|
|
62
|
+
slience,
|
|
63
|
+
patch: true
|
|
64
|
+
})
|
|
65
|
+
await Promise.all(chunk(values, chunkSize).map(getSelect))
|
|
74
66
|
if (this.ordering.length === 0)
|
|
75
|
-
this.
|
|
67
|
+
this.order({ [field]: values } as any)
|
|
76
68
|
}
|
|
77
69
|
|
|
78
70
|
|
|
79
|
-
async selectIDs(
|
|
80
|
-
ids: R[this['idField']][],
|
|
81
|
-
{ where, confirm, notify, slience }: selectOptions<R> = {},
|
|
82
|
-
chunkSize = 100,
|
|
83
|
-
) {
|
|
84
|
-
this.clear()
|
|
85
|
-
this.patchIDs(
|
|
86
|
-
ids,
|
|
87
|
-
{ where, confirm, notify, slience },
|
|
88
|
-
chunkSize
|
|
89
|
-
)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
71
|
|
|
93
72
|
}
|
|
94
73
|
|
package/src/utils/supabase.ts
CHANGED
|
@@ -137,7 +137,7 @@ export function handleError<T>(data: T[] | null, error: PostgrestError | null):
|
|
|
137
137
|
|
|
138
138
|
|
|
139
139
|
|
|
140
|
-
const _filterBuilder = supabase.from('DEMO').select()
|
|
140
|
+
const _filterBuilder = supabase.from('DEMO').select().eq('','').order('f').limit(3)
|
|
141
141
|
export type PostgrestFilterBuilder = typeof _filterBuilder
|
|
142
142
|
|
|
143
143
|
|