evikit 2.11.1 → 2.12.0
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/.woodpecker/test.yaml +38 -38
- package/README.md +6 -6
- package/db.d.ts +272 -265
- package/db.js +128 -128
- package/package.json +1 -1
- package/sqlite.js +34 -18
package/.woodpecker/test.yaml
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
when:
|
|
2
|
-
- event: push
|
|
3
|
-
branch: main
|
|
4
|
-
- event: pull_request
|
|
5
|
-
|
|
6
|
-
steps:
|
|
7
|
-
- name: build
|
|
8
|
-
image: node:24-alpine
|
|
9
|
-
commands:
|
|
10
|
-
- apk add git
|
|
11
|
-
# Are types correct?
|
|
12
|
-
- npm i
|
|
13
|
-
- npm run lint
|
|
14
|
-
- npx tsc
|
|
15
|
-
# Does todo app example build?
|
|
16
|
-
- npm create evikit my-app
|
|
17
|
-
- rm -rf my-app/node_modules/evikit
|
|
18
|
-
- (cd my-app/node_modules; git clone ../.. evikit)
|
|
19
|
-
- (cd my-app; npm run build)
|
|
20
|
-
# Does real-world application pass tests?
|
|
21
|
-
- git clone https://codeberg.org/nykula/lanquiz
|
|
22
|
-
- (cd lanquiz; npm i)
|
|
23
|
-
- rm -rf lanquiz/node_modules/evikit
|
|
24
|
-
- (cd lanquiz/node_modules; git clone ../.. evikit)
|
|
25
|
-
- (cd lanquiz; npm run build)
|
|
26
|
-
- (cd lanquiz; npm test -- --test-concurrency=1)
|
|
27
|
-
# Do other real-world applications build? 1/2
|
|
28
|
-
- git clone https://codeberg.org/nykula/mobhub
|
|
29
|
-
- (cd mobhub; npm i)
|
|
30
|
-
- rm -rf mobhub/node_modules/evikit
|
|
31
|
-
- (cd mobhub/node_modules; git clone ../.. evikit)
|
|
32
|
-
- (cd mobhub; npm run build)
|
|
33
|
-
# Do other real-world applications build? 2/2
|
|
34
|
-
- git clone https://codeberg.org/nykula/yoself
|
|
35
|
-
- (cd yoself; npm i)
|
|
36
|
-
- rm -rf yoself/node_modules/evikit
|
|
37
|
-
- (cd yoself/node_modules; git clone ../.. evikit)
|
|
38
|
-
- (cd yoself; npm run build)
|
|
1
|
+
when:
|
|
2
|
+
- event: push
|
|
3
|
+
branch: main
|
|
4
|
+
- event: pull_request
|
|
5
|
+
|
|
6
|
+
steps:
|
|
7
|
+
- name: build
|
|
8
|
+
image: node:24-alpine
|
|
9
|
+
commands:
|
|
10
|
+
- apk add git
|
|
11
|
+
# Are types correct?
|
|
12
|
+
- npm i
|
|
13
|
+
- npm run lint
|
|
14
|
+
- npx tsc
|
|
15
|
+
# Does todo app example build?
|
|
16
|
+
- npm create evikit my-app
|
|
17
|
+
- rm -rf my-app/node_modules/evikit
|
|
18
|
+
- (cd my-app/node_modules; git clone ../.. evikit)
|
|
19
|
+
- (cd my-app; npm run build)
|
|
20
|
+
# Does real-world application pass tests?
|
|
21
|
+
- git clone https://codeberg.org/nykula/lanquiz
|
|
22
|
+
- (cd lanquiz; npm i)
|
|
23
|
+
- rm -rf lanquiz/node_modules/evikit
|
|
24
|
+
- (cd lanquiz/node_modules; git clone ../.. evikit)
|
|
25
|
+
- (cd lanquiz; npm run build)
|
|
26
|
+
- (cd lanquiz; npm test -- --test-concurrency=1)
|
|
27
|
+
# Do other real-world applications build? 1/2
|
|
28
|
+
- git clone https://codeberg.org/nykula/mobhub
|
|
29
|
+
- (cd mobhub; npm i)
|
|
30
|
+
- rm -rf mobhub/node_modules/evikit
|
|
31
|
+
- (cd mobhub/node_modules; git clone ../.. evikit)
|
|
32
|
+
- (cd mobhub; npm run build)
|
|
33
|
+
# Do other real-world applications build? 2/2
|
|
34
|
+
- git clone https://codeberg.org/nykula/yoself
|
|
35
|
+
- (cd yoself; npm i)
|
|
36
|
+
- rm -rf yoself/node_modules/evikit
|
|
37
|
+
- (cd yoself/node_modules; git clone ../.. evikit)
|
|
38
|
+
- (cd yoself; npm run build)
|
package/README.md
CHANGED
|
@@ -473,8 +473,8 @@ export const QuestionIdServer = {
|
|
|
473
473
|
get: endpoint(QuestionIdGet, async ({ input }) => {
|
|
474
474
|
const i18n = await getI18n(input.lang);
|
|
475
475
|
const [question] = db.Question.select({
|
|
476
|
+
id: input.id,
|
|
476
477
|
with: { Choice: true },
|
|
477
|
-
where: { id: input.id },
|
|
478
478
|
});
|
|
479
479
|
// It maps join results to objects, for example you can get:
|
|
480
480
|
// question?.Choice[0]?.text
|
|
@@ -557,7 +557,7 @@ export const ChoiceServer = {
|
|
|
557
557
|
post: endpoint(ChoicePost, async ({ input }) => {
|
|
558
558
|
const i18n = await getI18n(input.lang);
|
|
559
559
|
|
|
560
|
-
const [question] = db.Question.select({
|
|
560
|
+
const [question] = db.Question.select({ id: input.questionId });
|
|
561
561
|
if (!question) {
|
|
562
562
|
return text(i18n.gettext("Question does not exist"), 400);
|
|
563
563
|
}
|
|
@@ -638,7 +638,7 @@ export const VoteServer = {
|
|
|
638
638
|
post: endpoint(VotePost, async ({ input }) => {
|
|
639
639
|
const i18n = await getI18n(input.lang);
|
|
640
640
|
|
|
641
|
-
const [choice] = db.Choice.select({
|
|
641
|
+
const [choice] = db.Choice.select({ id: input.choiceId });
|
|
642
642
|
if (!choice) {
|
|
643
643
|
return text(i18n.gettext("You didn't select a choice"), 400);
|
|
644
644
|
}
|
|
@@ -648,9 +648,9 @@ export const VoteServer = {
|
|
|
648
648
|
}
|
|
649
649
|
|
|
650
650
|
db.Choice.update({
|
|
651
|
+
id: choice.id,
|
|
651
652
|
set: { votes: choice.votes + 1 },
|
|
652
|
-
|
|
653
|
-
// To update multiple choices, pass an array of ids to `where.id`.
|
|
653
|
+
// To update multiple choices, pass an array of ids to `id`.
|
|
654
654
|
});
|
|
655
655
|
// To delete a choice: `db.Choice.delete({ id: choice.id })`.
|
|
656
656
|
// To delete multiple choices, pass an array of ids to `id`.
|
|
@@ -790,8 +790,8 @@ import { ok } from "node:assert/strict";
|
|
|
790
790
|
// ...
|
|
791
791
|
|
|
792
792
|
const [question] = db.Question.select({
|
|
793
|
+
id: input.id,
|
|
793
794
|
with: { Choice: { with: { iconStorage: true } } },
|
|
794
|
-
where: { id: input.id },
|
|
795
795
|
});
|
|
796
796
|
|
|
797
797
|
// ...
|
package/db.d.ts
CHANGED
|
@@ -1,265 +1,272 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
InferInput,
|
|
3
|
-
InferMetadata,
|
|
4
|
-
InferOutput,
|
|
5
|
-
MetadataAction,
|
|
6
|
-
ObjectSchema,
|
|
7
|
-
} from "valibot";
|
|
8
|
-
|
|
9
|
-
export type Db<
|
|
10
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
-
Schema extends Record<string, ObjectSchema<any, any>>,
|
|
12
|
-
R extends Relations<Schema> = Relations<Schema>,
|
|
13
|
-
F extends Flatten<Schema> = Flatten<Schema>,
|
|
14
|
-
> = {
|
|
15
|
-
[Table in keyof R]: Table extends keyof F & keyof Schema & string
|
|
16
|
-
? {
|
|
17
|
-
delete(where: Where<Schema, Table>): void;
|
|
18
|
-
|
|
19
|
-
select<
|
|
20
|
-
W extends Partial<Join<R, Table>> &
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
> =
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
:
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
>(
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
>(
|
|
1
|
+
import type {
|
|
2
|
+
InferInput,
|
|
3
|
+
InferMetadata,
|
|
4
|
+
InferOutput,
|
|
5
|
+
MetadataAction,
|
|
6
|
+
ObjectSchema,
|
|
7
|
+
} from "valibot";
|
|
8
|
+
|
|
9
|
+
export type Db<
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
+
Schema extends Record<string, ObjectSchema<any, any>>,
|
|
12
|
+
R extends Relations<Schema> = Relations<Schema>,
|
|
13
|
+
F extends Flatten<Schema> = Flatten<Schema>,
|
|
14
|
+
> = {
|
|
15
|
+
[Table in keyof R]: Table extends keyof F & keyof Schema & string
|
|
16
|
+
? {
|
|
17
|
+
delete(where: Where<Schema, Table>): void;
|
|
18
|
+
|
|
19
|
+
select<
|
|
20
|
+
W extends Partial<Join<R, Table>> &
|
|
21
|
+
Where<Schema, Table> & {
|
|
22
|
+
limit?: number;
|
|
23
|
+
offset?: number;
|
|
24
|
+
order?: Order<Schema, Table>;
|
|
25
|
+
/** @deprecated */ where?: Where<Schema, Table>;
|
|
26
|
+
},
|
|
27
|
+
>(
|
|
28
|
+
props?: W,
|
|
29
|
+
): Select<Schema, R, F, Table, W>[];
|
|
30
|
+
|
|
31
|
+
update(
|
|
32
|
+
query: Where<Schema, Table> & {
|
|
33
|
+
set: Partial<InferOutput<Schema[Table]>>;
|
|
34
|
+
/** @deprecated */ where?: Where<Schema, Table>;
|
|
35
|
+
},
|
|
36
|
+
): void;
|
|
37
|
+
|
|
38
|
+
upsert(
|
|
39
|
+
...values: InferInput<Schema[Table]>[]
|
|
40
|
+
): InferOutput<Schema[Table]>[];
|
|
41
|
+
}
|
|
42
|
+
: never;
|
|
43
|
+
} & {
|
|
44
|
+
schema: Schema;
|
|
45
|
+
subscribe(onMessage: ({ table }: { table: string }) => void): () => void;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export type Flatten<
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
50
|
+
Schema extends Record<string, ObjectSchema<any, any>>,
|
|
51
|
+
R extends Relations<Schema> = Relations<Schema>,
|
|
52
|
+
> = {
|
|
53
|
+
[Self in keyof R]: Self extends string
|
|
54
|
+
? UnionToIntersection<
|
|
55
|
+
{
|
|
56
|
+
[Own in keyof R[Self]]: Own extends string
|
|
57
|
+
? {
|
|
58
|
+
[Other in keyof R[Self][Own]]: Other extends string
|
|
59
|
+
? R[Self][Own][Other] extends Record<string, { name: string }>
|
|
60
|
+
? {
|
|
61
|
+
[
|
|
62
|
+
Its in keyof R[Self][Own][Other] as R[Self][Own][Other][Its]["name"]
|
|
63
|
+
]: Its extends string
|
|
64
|
+
? R[Self][Own][Other][Its]
|
|
65
|
+
: never;
|
|
66
|
+
}
|
|
67
|
+
: never
|
|
68
|
+
: never;
|
|
69
|
+
}[keyof R[Self][Own]]
|
|
70
|
+
: never;
|
|
71
|
+
}[keyof R[Self]]
|
|
72
|
+
>
|
|
73
|
+
: never;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export type Join<Relations, Self extends keyof Relations> = UnionToIntersection<
|
|
77
|
+
{
|
|
78
|
+
[Own in keyof Relations[Self]]: Own extends string
|
|
79
|
+
? {
|
|
80
|
+
[Other in keyof Relations[Self][Own]]: Other extends keyof Relations
|
|
81
|
+
? {
|
|
82
|
+
[Its in keyof Relations[Self][Own][Other]]: Its extends string
|
|
83
|
+
? Relations[Self][Own][Other] extends Record<
|
|
84
|
+
string,
|
|
85
|
+
{ name: string }
|
|
86
|
+
>
|
|
87
|
+
? {
|
|
88
|
+
// Turning this into partial record would cause infinite depth.
|
|
89
|
+
// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style
|
|
90
|
+
with: {
|
|
91
|
+
[K in Relations[Self][Own][Other][Its]["name"]]?:
|
|
92
|
+
Join<Relations, Other> | true;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
: never
|
|
96
|
+
: never;
|
|
97
|
+
}[keyof Relations[Self][Own][Other]]
|
|
98
|
+
: never;
|
|
99
|
+
}[keyof Relations[Self][Own]]
|
|
100
|
+
: never;
|
|
101
|
+
}[keyof Relations[Self]]
|
|
102
|
+
>;
|
|
103
|
+
|
|
104
|
+
export type Order<
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
106
|
+
Schema extends Record<string, ObjectSchema<any, any>>,
|
|
107
|
+
Table extends keyof Schema,
|
|
108
|
+
> =
|
|
109
|
+
| {
|
|
110
|
+
[K in keyof Schema[Table]["entries"]]?: null | true | { desc: true };
|
|
111
|
+
}
|
|
112
|
+
| {
|
|
113
|
+
[K in keyof Schema[Table]["entries"]]?: null | true | { desc: true };
|
|
114
|
+
}[];
|
|
115
|
+
|
|
116
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
117
|
+
export type Relations<Schema extends Record<string, ObjectSchema<any, any>>> = {
|
|
118
|
+
[Self in keyof Schema]: Self extends string
|
|
119
|
+
? OmitExtendsRecordUndefined<{
|
|
120
|
+
[Own in keyof Schema[Self]["entries"]]: Own extends string
|
|
121
|
+
? OmitExtendsRecordUndefined<{
|
|
122
|
+
[Other in keyof Schema]: Other extends string
|
|
123
|
+
? OmitExtendsUndefined<{
|
|
124
|
+
[Its in keyof Schema[Other]["entries"]]: Its extends string
|
|
125
|
+
? InferMetadata<Schema[Self]["entries"][Own]> extends {
|
|
126
|
+
key: Schema[Other]["entries"][Its];
|
|
127
|
+
references: Schema[Other];
|
|
128
|
+
}
|
|
129
|
+
? Schema[Other] extends InferMetadata<
|
|
130
|
+
Schema[Self]["entries"][Own]
|
|
131
|
+
>["references"]
|
|
132
|
+
? Schema[Other]["entries"][Its] extends InferMetadata<
|
|
133
|
+
Schema[Self]["entries"][Own]
|
|
134
|
+
>["key"]
|
|
135
|
+
? {
|
|
136
|
+
Its: Its;
|
|
137
|
+
name: Rewrite<Self, Own, Other, Its>;
|
|
138
|
+
Other: Other;
|
|
139
|
+
Own: Own;
|
|
140
|
+
type: "references";
|
|
141
|
+
}
|
|
142
|
+
: undefined
|
|
143
|
+
: undefined
|
|
144
|
+
: InferMetadata<Schema[Other]["entries"][Its]> extends {
|
|
145
|
+
key: Schema[Self]["entries"][Own];
|
|
146
|
+
references: Schema[Self];
|
|
147
|
+
}
|
|
148
|
+
? Schema[Self] extends InferMetadata<
|
|
149
|
+
Schema[Other]["entries"][Its]
|
|
150
|
+
>["references"]
|
|
151
|
+
? Schema[Self]["entries"][Own] extends InferMetadata<
|
|
152
|
+
Schema[Other]["entries"][Its]
|
|
153
|
+
>["key"]
|
|
154
|
+
? {
|
|
155
|
+
Its: Its;
|
|
156
|
+
name: Rewrite<Self, Own, Other, Its>;
|
|
157
|
+
Other: Other;
|
|
158
|
+
Own: Own;
|
|
159
|
+
type: "referencedBy";
|
|
160
|
+
}
|
|
161
|
+
: undefined
|
|
162
|
+
: undefined
|
|
163
|
+
: undefined
|
|
164
|
+
: undefined;
|
|
165
|
+
}>
|
|
166
|
+
: undefined;
|
|
167
|
+
}>
|
|
168
|
+
: undefined;
|
|
169
|
+
}>
|
|
170
|
+
: undefined;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export type Where<
|
|
174
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
175
|
+
Schema extends Record<string, ObjectSchema<any, any>>,
|
|
176
|
+
Table extends keyof Schema,
|
|
177
|
+
> = {
|
|
178
|
+
[K in keyof Schema[Table]["entries"]]?:
|
|
179
|
+
| InferOutput<Schema[Table]["entries"][K]>
|
|
180
|
+
| InferOutput<Schema[Table]["entries"][K]>[]
|
|
181
|
+
| Partial<
|
|
182
|
+
Record<
|
|
183
|
+
"gt" | "gte" | "lt" | "lte" | "not",
|
|
184
|
+
InferOutput<Schema[Table]["entries"][K]>
|
|
185
|
+
>
|
|
186
|
+
>;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
type OmitExtends<T, U> = {
|
|
190
|
+
[K in keyof T as T[K] extends U ? never : K]: T[K];
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
type OmitExtendsRecordUndefined<T> = OmitExtends<T, Record<string, undefined>>;
|
|
194
|
+
|
|
195
|
+
type OmitExtendsUndefined<T> = OmitExtends<T, undefined>;
|
|
196
|
+
|
|
197
|
+
type Rewrite<
|
|
198
|
+
Self extends string,
|
|
199
|
+
Own extends string,
|
|
200
|
+
Other extends string,
|
|
201
|
+
Its extends string,
|
|
202
|
+
> =
|
|
203
|
+
// Image.id, Question.imageId -> Image.Question.
|
|
204
|
+
Its extends `${Uncapitalize<Self>}${Capitalize<Own>}`
|
|
205
|
+
? Other
|
|
206
|
+
: // Quiz.coverId, Image.id -> Quiz.cover.
|
|
207
|
+
Own extends `${infer R}${Capitalize<Its>}`
|
|
208
|
+
? R
|
|
209
|
+
: // Image.id, Quiz.coverId -> Image.CoverQuiz.
|
|
210
|
+
Its extends `${infer R}${Capitalize<Own>}`
|
|
211
|
+
? `${Capitalize<R>}${Other}`
|
|
212
|
+
: never;
|
|
213
|
+
|
|
214
|
+
type Select<
|
|
215
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
216
|
+
Schema extends Record<string, ObjectSchema<any, any>>,
|
|
217
|
+
R extends Relations<Schema> = Relations<Schema>,
|
|
218
|
+
F extends Flatten<Schema> = Flatten<Schema>,
|
|
219
|
+
Table extends keyof F & keyof Schema = keyof F & keyof Schema,
|
|
220
|
+
W extends Join<R, Table> = Join<R, Table>,
|
|
221
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
222
|
+
> = W extends { with: any }
|
|
223
|
+
? InferOutput<Schema[Table]> & {
|
|
224
|
+
[K in keyof W["with"]]: K extends keyof F[Table]
|
|
225
|
+
? F[Table][K] extends {
|
|
226
|
+
Other: keyof F & keyof Schema;
|
|
227
|
+
type: "referencedBy";
|
|
228
|
+
}
|
|
229
|
+
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
230
|
+
(W["with"][K] extends { with: any }
|
|
231
|
+
? Select<Schema, R, F, F[Table][K]["Other"], W["with"][K]>
|
|
232
|
+
: InferOutput<Schema[F[Table][K]["Other"]]>)[]
|
|
233
|
+
: F[Table][K] extends {
|
|
234
|
+
Other: keyof F & keyof Schema;
|
|
235
|
+
type: "references";
|
|
236
|
+
}
|
|
237
|
+
? | undefined
|
|
238
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
239
|
+
| (W["with"][K] extends { with: any }
|
|
240
|
+
? Select<Schema, R, F, F[Table][K]["Other"], W["with"][K]>
|
|
241
|
+
: InferOutput<Schema[F[Table][K]["Other"]]>)
|
|
242
|
+
: never
|
|
243
|
+
: never;
|
|
244
|
+
}
|
|
245
|
+
: InferOutput<Schema[Table]>;
|
|
246
|
+
|
|
247
|
+
// https://www.geeksforgeeks.org/typescript/union-type-to-intersection-type-in-typescript/
|
|
248
|
+
type UnionToIntersection<U> = (
|
|
249
|
+
U extends object ? (k: U) => void : never
|
|
250
|
+
) extends (k: infer I) => void
|
|
251
|
+
? I
|
|
252
|
+
: never;
|
|
253
|
+
|
|
254
|
+
export function flatten<
|
|
255
|
+
Schema extends Record<string, ObjectSchema<any, any>>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
256
|
+
>(relations: Relations<Schema>): Flatten<Schema>;
|
|
257
|
+
|
|
258
|
+
export function primary<T>(): MetadataAction<T, { primary: true }>;
|
|
259
|
+
|
|
260
|
+
export function references<
|
|
261
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
262
|
+
R extends { entries: any },
|
|
263
|
+
K extends keyof R["entries"],
|
|
264
|
+
T,
|
|
265
|
+
>(
|
|
266
|
+
references: R,
|
|
267
|
+
key: K,
|
|
268
|
+
): MetadataAction<T, { key: R["entries"][K]; references: R }>;
|
|
269
|
+
|
|
270
|
+
export function relations<
|
|
271
|
+
Schema extends Record<string, ObjectSchema<any, any>>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
272
|
+
>(schema: Schema): Relations<Schema>;
|
package/db.js
CHANGED
|
@@ -1,128 +1,128 @@
|
|
|
1
|
-
import { getMetadata, metadata } from "valibot";
|
|
2
|
-
|
|
3
|
-
/** @type {typeof import('./db.d.ts').flatten} */
|
|
4
|
-
export function flatten(relations) {
|
|
5
|
-
return /** @type {any} */ (
|
|
6
|
-
Object.fromEntries(
|
|
7
|
-
Object.entries(relations).map(([Self, self]) => [
|
|
8
|
-
Self,
|
|
9
|
-
Object.fromEntries(
|
|
10
|
-
Object.values(self)
|
|
11
|
-
.map((own) =>
|
|
12
|
-
Object.values(own).map((other) =>
|
|
13
|
-
Object.values(other).map((its) => [its.name, its]),
|
|
14
|
-
),
|
|
15
|
-
)
|
|
16
|
-
.flat()
|
|
17
|
-
.flat(),
|
|
18
|
-
),
|
|
19
|
-
]),
|
|
20
|
-
)
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function primary() {
|
|
25
|
-
return metadata({ primary: true });
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** @type {typeof import('./db.d.ts').references} */
|
|
29
|
-
export function references(references, key) {
|
|
30
|
-
return metadata({ key: references.entries[key], references });
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** @type {typeof import('./db.d.ts').relations} */
|
|
34
|
-
export function relations(schema) {
|
|
35
|
-
return /** @type {any} */ (
|
|
36
|
-
Object.fromEntries(
|
|
37
|
-
Object.entries(/** @type {any} */ (schema)).map(([Self, self]) => [
|
|
38
|
-
Self,
|
|
39
|
-
Object.fromEntries(
|
|
40
|
-
Object.entries(self.entries)
|
|
41
|
-
.map(([Own, own]) => [
|
|
42
|
-
Own,
|
|
43
|
-
Object.fromEntries(
|
|
44
|
-
Object.entries(/** @type {any} */ (schema))
|
|
45
|
-
.map(([Other, other]) => [
|
|
46
|
-
Other,
|
|
47
|
-
Object.fromEntries(
|
|
48
|
-
Object.entries(other.entries)
|
|
49
|
-
.map(([Its, its]) => {
|
|
50
|
-
const ownMetadata =
|
|
51
|
-
/** @type {{ key?: string, references: any }} */ (
|
|
52
|
-
getMetadata(own)
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
const itsMetadata =
|
|
56
|
-
/** @type {{ key?: string, references: any }} */ (
|
|
57
|
-
getMetadata(its)
|
|
58
|
-
);
|
|
59
|
-
return [
|
|
60
|
-
Its,
|
|
61
|
-
ownMetadata.key == its &&
|
|
62
|
-
other == ownMetadata.references
|
|
63
|
-
? {
|
|
64
|
-
Its: Its,
|
|
65
|
-
name: rewrite(Self, Own, Other, Its),
|
|
66
|
-
Other: Other,
|
|
67
|
-
Own: Own,
|
|
68
|
-
type: "references",
|
|
69
|
-
}
|
|
70
|
-
: itsMetadata.key == own &&
|
|
71
|
-
self == itsMetadata.references
|
|
72
|
-
? {
|
|
73
|
-
Its: Its,
|
|
74
|
-
name: rewrite(Self, Own, Other, Its),
|
|
75
|
-
Other: Other,
|
|
76
|
-
Own: Own,
|
|
77
|
-
type: "referencedBy",
|
|
78
|
-
}
|
|
79
|
-
: undefined,
|
|
80
|
-
];
|
|
81
|
-
})
|
|
82
|
-
.filter(([, maybeUndefined]) => maybeUndefined),
|
|
83
|
-
),
|
|
84
|
-
])
|
|
85
|
-
.filter(([, maybeRecordUndefined]) =>
|
|
86
|
-
Object.values(maybeRecordUndefined).some((value) => value),
|
|
87
|
-
),
|
|
88
|
-
),
|
|
89
|
-
])
|
|
90
|
-
.filter(([, maybeRecordUndefined]) =>
|
|
91
|
-
Object.values(maybeRecordUndefined).some((value) => value),
|
|
92
|
-
),
|
|
93
|
-
),
|
|
94
|
-
]),
|
|
95
|
-
)
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/** @param {string} input */
|
|
100
|
-
function capitalize(input) {
|
|
101
|
-
return `${input[0]?.toUpperCase()}${input.slice(1)}`;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* @param {string} Self
|
|
106
|
-
* @param {string} Own
|
|
107
|
-
* @param {string} Other
|
|
108
|
-
* @param {string} Its
|
|
109
|
-
*/
|
|
110
|
-
function rewrite(Self, Own, Other, Its) {
|
|
111
|
-
return (
|
|
112
|
-
// Image.id, Question.imageId -> Image.Question.
|
|
113
|
-
Its == `${uncapitalize(Self)}${capitalize(Own)}`
|
|
114
|
-
? Other
|
|
115
|
-
: // Quiz.coverId, Image.id -> Quiz.cover.
|
|
116
|
-
Own.endsWith(capitalize(Its))
|
|
117
|
-
? Own.slice(0, -Its.length)
|
|
118
|
-
: // Image.id, Quiz.coverId -> Image.CoverQuiz.
|
|
119
|
-
Its.endsWith(capitalize(Own))
|
|
120
|
-
? `${capitalize(Its.slice(0, -Own.length))}${Other}`
|
|
121
|
-
: undefined
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/** @param {string} input */
|
|
126
|
-
function uncapitalize(input) {
|
|
127
|
-
return `${input[0]?.toLowerCase()}${input.slice(1)}`;
|
|
128
|
-
}
|
|
1
|
+
import { getMetadata, metadata } from "valibot";
|
|
2
|
+
|
|
3
|
+
/** @type {typeof import('./db.d.ts').flatten} */
|
|
4
|
+
export function flatten(relations) {
|
|
5
|
+
return /** @type {any} */ (
|
|
6
|
+
Object.fromEntries(
|
|
7
|
+
Object.entries(relations).map(([Self, self]) => [
|
|
8
|
+
Self,
|
|
9
|
+
Object.fromEntries(
|
|
10
|
+
Object.values(self)
|
|
11
|
+
.map((own) =>
|
|
12
|
+
Object.values(own).map((other) =>
|
|
13
|
+
Object.values(other).map((its) => [its.name, its]),
|
|
14
|
+
),
|
|
15
|
+
)
|
|
16
|
+
.flat()
|
|
17
|
+
.flat(),
|
|
18
|
+
),
|
|
19
|
+
]),
|
|
20
|
+
)
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function primary() {
|
|
25
|
+
return metadata({ primary: true });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** @type {typeof import('./db.d.ts').references} */
|
|
29
|
+
export function references(references, key) {
|
|
30
|
+
return metadata({ key: references.entries[key], references });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** @type {typeof import('./db.d.ts').relations} */
|
|
34
|
+
export function relations(schema) {
|
|
35
|
+
return /** @type {any} */ (
|
|
36
|
+
Object.fromEntries(
|
|
37
|
+
Object.entries(/** @type {any} */ (schema)).map(([Self, self]) => [
|
|
38
|
+
Self,
|
|
39
|
+
Object.fromEntries(
|
|
40
|
+
Object.entries(self.entries)
|
|
41
|
+
.map(([Own, own]) => [
|
|
42
|
+
Own,
|
|
43
|
+
Object.fromEntries(
|
|
44
|
+
Object.entries(/** @type {any} */ (schema))
|
|
45
|
+
.map(([Other, other]) => [
|
|
46
|
+
Other,
|
|
47
|
+
Object.fromEntries(
|
|
48
|
+
Object.entries(other.entries)
|
|
49
|
+
.map(([Its, its]) => {
|
|
50
|
+
const ownMetadata =
|
|
51
|
+
/** @type {{ key?: string, references: any }} */ (
|
|
52
|
+
getMetadata(own)
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const itsMetadata =
|
|
56
|
+
/** @type {{ key?: string, references: any }} */ (
|
|
57
|
+
getMetadata(its)
|
|
58
|
+
);
|
|
59
|
+
return [
|
|
60
|
+
Its,
|
|
61
|
+
ownMetadata.key == its &&
|
|
62
|
+
other == ownMetadata.references
|
|
63
|
+
? {
|
|
64
|
+
Its: Its,
|
|
65
|
+
name: rewrite(Self, Own, Other, Its),
|
|
66
|
+
Other: Other,
|
|
67
|
+
Own: Own,
|
|
68
|
+
type: "references",
|
|
69
|
+
}
|
|
70
|
+
: itsMetadata.key == own &&
|
|
71
|
+
self == itsMetadata.references
|
|
72
|
+
? {
|
|
73
|
+
Its: Its,
|
|
74
|
+
name: rewrite(Self, Own, Other, Its),
|
|
75
|
+
Other: Other,
|
|
76
|
+
Own: Own,
|
|
77
|
+
type: "referencedBy",
|
|
78
|
+
}
|
|
79
|
+
: undefined,
|
|
80
|
+
];
|
|
81
|
+
})
|
|
82
|
+
.filter(([, maybeUndefined]) => maybeUndefined),
|
|
83
|
+
),
|
|
84
|
+
])
|
|
85
|
+
.filter(([, maybeRecordUndefined]) =>
|
|
86
|
+
Object.values(maybeRecordUndefined).some((value) => value),
|
|
87
|
+
),
|
|
88
|
+
),
|
|
89
|
+
])
|
|
90
|
+
.filter(([, maybeRecordUndefined]) =>
|
|
91
|
+
Object.values(maybeRecordUndefined).some((value) => value),
|
|
92
|
+
),
|
|
93
|
+
),
|
|
94
|
+
]),
|
|
95
|
+
)
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** @param {string} input */
|
|
100
|
+
function capitalize(input) {
|
|
101
|
+
return `${input[0]?.toUpperCase()}${input.slice(1)}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @param {string} Self
|
|
106
|
+
* @param {string} Own
|
|
107
|
+
* @param {string} Other
|
|
108
|
+
* @param {string} Its
|
|
109
|
+
*/
|
|
110
|
+
function rewrite(Self, Own, Other, Its) {
|
|
111
|
+
return (
|
|
112
|
+
// Image.id, Question.imageId -> Image.Question.
|
|
113
|
+
Its == `${uncapitalize(Self)}${capitalize(Own)}`
|
|
114
|
+
? Other
|
|
115
|
+
: // Quiz.coverId, Image.id -> Quiz.cover.
|
|
116
|
+
Own.endsWith(capitalize(Its))
|
|
117
|
+
? Own.slice(0, -Its.length)
|
|
118
|
+
: // Image.id, Quiz.coverId -> Image.CoverQuiz.
|
|
119
|
+
Its.endsWith(capitalize(Own))
|
|
120
|
+
? `${capitalize(Its.slice(0, -Own.length))}${Other}`
|
|
121
|
+
: undefined
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** @param {string} input */
|
|
126
|
+
function uncapitalize(input) {
|
|
127
|
+
return `${input[0]?.toLowerCase()}${input.slice(1)}`;
|
|
128
|
+
}
|
package/package.json
CHANGED
package/sqlite.js
CHANGED
|
@@ -323,7 +323,7 @@ export function sqlite({ pkg, schema }) {
|
|
|
323
323
|
delete(where) {
|
|
324
324
|
if (
|
|
325
325
|
database
|
|
326
|
-
.prepare(`delete from ${table}` + whereQuery(
|
|
326
|
+
.prepare(`delete from ${table}` + whereQuery(where))
|
|
327
327
|
.run(...whereValues(where)).changes
|
|
328
328
|
) {
|
|
329
329
|
publish({ table });
|
|
@@ -335,21 +335,23 @@ export function sqlite({ pkg, schema }) {
|
|
|
335
335
|
return select({ database, flat, join, schema, table });
|
|
336
336
|
},
|
|
337
337
|
|
|
338
|
-
/** @param {{ set: Partial<import("valibot").InferOutput<(typeof schema)[typeof table]>>, where
|
|
339
|
-
update(
|
|
338
|
+
/** @param {import("./db.d.ts").Where<typeof schema, typeof table> & { set: Partial<import("valibot").InferOutput<(typeof schema)[typeof table]>>, where?: import("./db.d.ts").Where<typeof schema, typeof table> }} query */
|
|
339
|
+
update(query) {
|
|
340
340
|
if (
|
|
341
341
|
database
|
|
342
342
|
.prepare(
|
|
343
343
|
`update ${table} set\n` +
|
|
344
|
-
Object.keys(set)
|
|
344
|
+
Object.keys(query.set)
|
|
345
345
|
.map((name) => `${name} = ?`)
|
|
346
346
|
.join(",\n") +
|
|
347
|
-
whereQuery(
|
|
347
|
+
whereQuery(wherePropertyOrSelf(query)),
|
|
348
348
|
)
|
|
349
349
|
.run(
|
|
350
350
|
...[
|
|
351
|
-
...Object.values(set).map(
|
|
352
|
-
|
|
351
|
+
...Object.values(query.set).map(
|
|
352
|
+
(value) => value ?? null,
|
|
353
|
+
),
|
|
354
|
+
...whereValues(wherePropertyOrSelf(query)),
|
|
353
355
|
],
|
|
354
356
|
).changes
|
|
355
357
|
) {
|
|
@@ -391,12 +393,12 @@ export function sqlite({ pkg, schema }) {
|
|
|
391
393
|
};
|
|
392
394
|
}
|
|
393
395
|
|
|
394
|
-
/** @type {<Schema extends Record<string, import("valibot").ObjectSchema<any, any>>, R extends import("./db.d.ts").Relations<Schema> = import("./db.d.ts").Relations<Schema>, F extends import("./db.d.ts").Flatten<Schema> = import("./db.d.ts").Flatten<Schema>, Table extends keyof F & keyof Schema = keyof F & keyof Schema >(props: { database: DatabaseSync; flat: F; join?: import("./db.d.ts").Join<R, Table> & { limit?: number, offset?: number, order?: import("./db.d.ts").Order<Schema, Table>, where?: import("./db.d.ts").Where<Schema, Table> }; schema: Schema; table: Table; }) => any[]} */
|
|
396
|
+
/** @type {<Schema extends Record<string, import("valibot").ObjectSchema<any, any>>, R extends import("./db.d.ts").Relations<Schema> = import("./db.d.ts").Relations<Schema>, F extends import("./db.d.ts").Flatten<Schema> = import("./db.d.ts").Flatten<Schema>, Table extends keyof F & keyof Schema = keyof F & keyof Schema >(props: { database: DatabaseSync; flat: F; join?: import("./db.d.ts").Join<R, Table> & import("./db.d.ts").Where<Schema, Table> & { limit?: number, offset?: number, order?: import("./db.d.ts").Order<Schema, Table>, where?: import("./db.d.ts").Where<Schema, Table> }; schema: Schema; table: Table; }) => any[]} */
|
|
395
397
|
function select(props) {
|
|
396
398
|
ok(typeof props.table == "string");
|
|
397
399
|
let sql =
|
|
398
400
|
`select * from ${props.table}` +
|
|
399
|
-
whereQuery(
|
|
401
|
+
whereQuery(wherePropertyOrSelf(props.join)) +
|
|
400
402
|
(props.join?.order
|
|
401
403
|
? "\norder by " +
|
|
402
404
|
(Array.isArray(props.join.order)
|
|
@@ -405,7 +407,7 @@ function select(props) {
|
|
|
405
407
|
)
|
|
406
408
|
.flatMap((column) =>
|
|
407
409
|
Object.entries(column).map(([name, props]) =>
|
|
408
|
-
props?.desc ? `${name} desc` : name,
|
|
410
|
+
typeof props == "object" && props?.desc ? `${name} desc` : name,
|
|
409
411
|
),
|
|
410
412
|
)
|
|
411
413
|
.join(", ")
|
|
@@ -469,7 +471,7 @@ function select(props) {
|
|
|
469
471
|
|
|
470
472
|
const rows = props.database
|
|
471
473
|
.prepare(sql)
|
|
472
|
-
.all(...whereValues(props.join
|
|
474
|
+
.all(...whereValues(wherePropertyOrSelf(props.join)));
|
|
473
475
|
if (!(/** @type {any} */ (props.join)?.with)) {
|
|
474
476
|
for (const row of rows) {
|
|
475
477
|
for (const name of Object.keys(row)) {
|
|
@@ -543,15 +545,28 @@ function select(props) {
|
|
|
543
545
|
return state.entities;
|
|
544
546
|
}
|
|
545
547
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
+
let deprecatedFound = false;
|
|
549
|
+
/** @param {{ where?: any } | undefined} props */
|
|
550
|
+
function wherePropertyOrSelf(props) {
|
|
551
|
+
if (props?.where) {
|
|
552
|
+
if (!deprecatedFound) {
|
|
553
|
+
deprecatedFound = true;
|
|
554
|
+
console.error(
|
|
555
|
+
"`where: {}` is deprecated, move its properties one level above",
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
return props.where;
|
|
559
|
+
}
|
|
560
|
+
return props;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/** @param {import("./db.js").Where<any, string> | undefined} where */
|
|
564
|
+
function whereQuery(where) {
|
|
548
565
|
return (
|
|
549
566
|
"\nwhere true\n" +
|
|
550
567
|
Object.entries(where || {})
|
|
568
|
+
.filter(([name]) => !/^(limit|offset|order|set|with)$/.test(name))
|
|
551
569
|
.map(([name, values]) => {
|
|
552
|
-
if (alias) {
|
|
553
|
-
name = `${alias}.${name}`;
|
|
554
|
-
}
|
|
555
570
|
return Array.isArray(values)
|
|
556
571
|
? `and ${name} in (${values.map(() => "?").join(", ")})`
|
|
557
572
|
: typeof values == "number" || typeof values == "string"
|
|
@@ -579,8 +594,9 @@ function whereQuery({ alias, where }) {
|
|
|
579
594
|
|
|
580
595
|
/** @param {import("./db.js").Where<any, string> | undefined} where */
|
|
581
596
|
function whereValues(where) {
|
|
582
|
-
return Object.
|
|
583
|
-
.
|
|
597
|
+
return Object.entries(where || {})
|
|
598
|
+
.filter(([name]) => !/^(limit|offset|order|set|with)$/.test(name))
|
|
599
|
+
.flatMap(([, values]) =>
|
|
584
600
|
Array.isArray(values)
|
|
585
601
|
? values
|
|
586
602
|
: typeof values == "number" || typeof values == "string"
|