evikit 2.10.1 → 2.11.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.
- package/.woodpecker/test.yaml +38 -26
- package/README.md +7 -6
- package/db.d.ts +265 -255
- package/db.js +128 -128
- package/package.json +1 -1
- package/sqlite.js +22 -7
package/.woodpecker/test.yaml
CHANGED
|
@@ -1,26 +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)
|
|
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
|
@@ -218,7 +218,7 @@ export function api() {
|
|
|
218
218
|
|
|
219
219
|
#### Page router
|
|
220
220
|
|
|
221
|
-
Add a Preact router at `src/
|
|
221
|
+
Add a Preact router at `src/prerender.js` (uncomment the routes one-by-one when we add them later):
|
|
222
222
|
|
|
223
223
|
```js
|
|
224
224
|
import { route, ssr } from "evikit";
|
|
@@ -252,7 +252,7 @@ Make an HTML template `index.html`, leaving placeholders for server-rendered con
|
|
|
252
252
|
</head>
|
|
253
253
|
<body>
|
|
254
254
|
<div id="app"><!--app-html--></div>
|
|
255
|
-
<script prerender type="module" src="/src/
|
|
255
|
+
<script prerender type="module" src="/src/prerender.js"></script>
|
|
256
256
|
</body>
|
|
257
257
|
</html>
|
|
258
258
|
```
|
|
@@ -308,7 +308,7 @@ import { readFile } from "node:fs/promises";
|
|
|
308
308
|
import swaggerUi from "swagger-ui-express";
|
|
309
309
|
|
|
310
310
|
import { api } from "./src/api.js";
|
|
311
|
-
import { prerender } from "./src/
|
|
311
|
+
import { prerender } from "./src/prerender.js";
|
|
312
312
|
|
|
313
313
|
const app = express();
|
|
314
314
|
app.use(api().onRequest);
|
|
@@ -433,7 +433,7 @@ export function HomePage() {
|
|
|
433
433
|
}
|
|
434
434
|
```
|
|
435
435
|
|
|
436
|
-
Uncomment the `HomePage` import and `/` route in `src/
|
|
436
|
+
Uncomment the `HomePage` import and `/` route in `src/prerender.js`.
|
|
437
437
|
|
|
438
438
|
#### Forms
|
|
439
439
|
|
|
@@ -524,7 +524,7 @@ export function QuestionIdPage() {
|
|
|
524
524
|
}
|
|
525
525
|
```
|
|
526
526
|
|
|
527
|
-
Uncomment the `QuestionIdPage` import and `/question/:id` route in `src/
|
|
527
|
+
Uncomment the `QuestionIdPage` import and `/question/:id` route in `src/prerender.js`.
|
|
528
528
|
|
|
529
529
|
##### Suggest own option
|
|
530
530
|
|
|
@@ -789,8 +789,9 @@ import { ok } from "node:assert/strict";
|
|
|
789
789
|
|
|
790
790
|
// ...
|
|
791
791
|
|
|
792
|
-
const
|
|
792
|
+
const [question] = db.Question.select({
|
|
793
793
|
with: { Choice: { with: { iconStorage: true } } },
|
|
794
|
+
where: { id: input.id },
|
|
794
795
|
});
|
|
795
796
|
|
|
796
797
|
// ...
|
package/db.d.ts
CHANGED
|
@@ -1,255 +1,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
|
-
|
|
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
|
-
): MetadataAction<T, {
|
|
252
|
-
|
|
253
|
-
export function
|
|
254
|
-
|
|
255
|
-
|
|
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
|
+
limit?: number;
|
|
22
|
+
offset?: number;
|
|
23
|
+
order?: Order<Schema, Table>;
|
|
24
|
+
where?: Where<Schema, Table>;
|
|
25
|
+
},
|
|
26
|
+
>(
|
|
27
|
+
props?: W,
|
|
28
|
+
): Select<Schema, R, F, Table, W>[];
|
|
29
|
+
|
|
30
|
+
update(query: {
|
|
31
|
+
set: Partial<InferOutput<Schema[Table]>>;
|
|
32
|
+
where: Where<Schema, Table>;
|
|
33
|
+
}): void;
|
|
34
|
+
|
|
35
|
+
upsert(
|
|
36
|
+
...values: InferInput<Schema[Table]>[]
|
|
37
|
+
): InferOutput<Schema[Table]>[];
|
|
38
|
+
}
|
|
39
|
+
: never;
|
|
40
|
+
} & {
|
|
41
|
+
schema: Schema;
|
|
42
|
+
subscribe(onMessage: ({ table }: { table: string }) => void): () => void;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type Flatten<
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
Schema extends Record<string, ObjectSchema<any, any>>,
|
|
48
|
+
R extends Relations<Schema> = Relations<Schema>,
|
|
49
|
+
> = {
|
|
50
|
+
[Self in keyof R]: Self extends string
|
|
51
|
+
? UnionToIntersection<
|
|
52
|
+
{
|
|
53
|
+
[Own in keyof R[Self]]: Own extends string
|
|
54
|
+
? {
|
|
55
|
+
[Other in keyof R[Self][Own]]: Other extends string
|
|
56
|
+
? R[Self][Own][Other] extends Record<string, { name: string }>
|
|
57
|
+
? {
|
|
58
|
+
[
|
|
59
|
+
Its in keyof R[Self][Own][Other] as R[Self][Own][Other][Its]["name"]
|
|
60
|
+
]: Its extends string
|
|
61
|
+
? R[Self][Own][Other][Its]
|
|
62
|
+
: never;
|
|
63
|
+
}
|
|
64
|
+
: never
|
|
65
|
+
: never;
|
|
66
|
+
}[keyof R[Self][Own]]
|
|
67
|
+
: never;
|
|
68
|
+
}[keyof R[Self]]
|
|
69
|
+
>
|
|
70
|
+
: never;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export type Join<Relations, Self extends keyof Relations> = UnionToIntersection<
|
|
74
|
+
{
|
|
75
|
+
[Own in keyof Relations[Self]]: Own extends string
|
|
76
|
+
? {
|
|
77
|
+
[Other in keyof Relations[Self][Own]]: Other extends keyof Relations
|
|
78
|
+
? {
|
|
79
|
+
[Its in keyof Relations[Self][Own][Other]]: Its extends string
|
|
80
|
+
? Relations[Self][Own][Other] extends Record<
|
|
81
|
+
string,
|
|
82
|
+
{ name: string }
|
|
83
|
+
>
|
|
84
|
+
? {
|
|
85
|
+
// Turning this into partial record would cause infinite depth.
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style
|
|
87
|
+
with: {
|
|
88
|
+
[K in Relations[Self][Own][Other][Its]["name"]]?:
|
|
89
|
+
Join<Relations, Other> | true;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
: never
|
|
93
|
+
: never;
|
|
94
|
+
}[keyof Relations[Self][Own][Other]]
|
|
95
|
+
: never;
|
|
96
|
+
}[keyof Relations[Self][Own]]
|
|
97
|
+
: never;
|
|
98
|
+
}[keyof Relations[Self]]
|
|
99
|
+
>;
|
|
100
|
+
|
|
101
|
+
export type Order<
|
|
102
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
103
|
+
Schema extends Record<string, ObjectSchema<any, any>>,
|
|
104
|
+
Table extends keyof Schema,
|
|
105
|
+
> =
|
|
106
|
+
| { [K in keyof Schema[Table]["entries"]]?: null | { desc: true } }
|
|
107
|
+
| { [K in keyof Schema[Table]["entries"]]?: null | { desc: true } }[];
|
|
108
|
+
|
|
109
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
110
|
+
export type Relations<Schema extends Record<string, ObjectSchema<any, any>>> = {
|
|
111
|
+
[Self in keyof Schema]: Self extends string
|
|
112
|
+
? OmitExtendsRecordUndefined<{
|
|
113
|
+
[Own in keyof Schema[Self]["entries"]]: Own extends string
|
|
114
|
+
? OmitExtendsRecordUndefined<{
|
|
115
|
+
[Other in keyof Schema]: Other extends string
|
|
116
|
+
? OmitExtendsUndefined<{
|
|
117
|
+
[Its in keyof Schema[Other]["entries"]]: Its extends string
|
|
118
|
+
? InferMetadata<Schema[Self]["entries"][Own]> extends {
|
|
119
|
+
key: Schema[Other]["entries"][Its];
|
|
120
|
+
references: Schema[Other];
|
|
121
|
+
}
|
|
122
|
+
? Schema[Other] extends InferMetadata<
|
|
123
|
+
Schema[Self]["entries"][Own]
|
|
124
|
+
>["references"]
|
|
125
|
+
? Schema[Other]["entries"][Its] extends InferMetadata<
|
|
126
|
+
Schema[Self]["entries"][Own]
|
|
127
|
+
>["key"]
|
|
128
|
+
? {
|
|
129
|
+
Its: Its;
|
|
130
|
+
name: Rewrite<Self, Own, Other, Its>;
|
|
131
|
+
Other: Other;
|
|
132
|
+
Own: Own;
|
|
133
|
+
type: "references";
|
|
134
|
+
}
|
|
135
|
+
: undefined
|
|
136
|
+
: undefined
|
|
137
|
+
: InferMetadata<Schema[Other]["entries"][Its]> extends {
|
|
138
|
+
key: Schema[Self]["entries"][Own];
|
|
139
|
+
references: Schema[Self];
|
|
140
|
+
}
|
|
141
|
+
? Schema[Self] extends InferMetadata<
|
|
142
|
+
Schema[Other]["entries"][Its]
|
|
143
|
+
>["references"]
|
|
144
|
+
? Schema[Self]["entries"][Own] extends InferMetadata<
|
|
145
|
+
Schema[Other]["entries"][Its]
|
|
146
|
+
>["key"]
|
|
147
|
+
? {
|
|
148
|
+
Its: Its;
|
|
149
|
+
name: Rewrite<Self, Own, Other, Its>;
|
|
150
|
+
Other: Other;
|
|
151
|
+
Own: Own;
|
|
152
|
+
type: "referencedBy";
|
|
153
|
+
}
|
|
154
|
+
: undefined
|
|
155
|
+
: undefined
|
|
156
|
+
: undefined
|
|
157
|
+
: undefined;
|
|
158
|
+
}>
|
|
159
|
+
: undefined;
|
|
160
|
+
}>
|
|
161
|
+
: undefined;
|
|
162
|
+
}>
|
|
163
|
+
: undefined;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export type Where<
|
|
167
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
168
|
+
Schema extends Record<string, ObjectSchema<any, any>>,
|
|
169
|
+
Table extends keyof Schema,
|
|
170
|
+
> = {
|
|
171
|
+
[K in keyof Schema[Table]["entries"]]?:
|
|
172
|
+
| InferOutput<Schema[Table]["entries"][K]>
|
|
173
|
+
| InferOutput<Schema[Table]["entries"][K]>[]
|
|
174
|
+
| Partial<
|
|
175
|
+
Record<
|
|
176
|
+
"gt" | "gte" | "lt" | "lte" | "not",
|
|
177
|
+
InferOutput<Schema[Table]["entries"][K]>
|
|
178
|
+
>
|
|
179
|
+
>;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
type OmitExtends<T, U> = {
|
|
183
|
+
[K in keyof T as T[K] extends U ? never : K]: T[K];
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
type OmitExtendsRecordUndefined<T> = OmitExtends<T, Record<string, undefined>>;
|
|
187
|
+
|
|
188
|
+
type OmitExtendsUndefined<T> = OmitExtends<T, undefined>;
|
|
189
|
+
|
|
190
|
+
type Rewrite<
|
|
191
|
+
Self extends string,
|
|
192
|
+
Own extends string,
|
|
193
|
+
Other extends string,
|
|
194
|
+
Its extends string,
|
|
195
|
+
> =
|
|
196
|
+
// Image.id, Question.imageId -> Image.Question.
|
|
197
|
+
Its extends `${Uncapitalize<Self>}${Capitalize<Own>}`
|
|
198
|
+
? Other
|
|
199
|
+
: // Quiz.coverId, Image.id -> Quiz.cover.
|
|
200
|
+
Own extends `${infer R}${Capitalize<Its>}`
|
|
201
|
+
? R
|
|
202
|
+
: // Image.id, Quiz.coverId -> Image.CoverQuiz.
|
|
203
|
+
Its extends `${infer R}${Capitalize<Own>}`
|
|
204
|
+
? `${Capitalize<R>}${Other}`
|
|
205
|
+
: never;
|
|
206
|
+
|
|
207
|
+
type Select<
|
|
208
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
209
|
+
Schema extends Record<string, ObjectSchema<any, any>>,
|
|
210
|
+
R extends Relations<Schema> = Relations<Schema>,
|
|
211
|
+
F extends Flatten<Schema> = Flatten<Schema>,
|
|
212
|
+
Table extends keyof F & keyof Schema = keyof F & keyof Schema,
|
|
213
|
+
W extends Join<R, Table> = Join<R, Table>,
|
|
214
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
215
|
+
> = W extends { with: any }
|
|
216
|
+
? InferOutput<Schema[Table]> & {
|
|
217
|
+
[K in keyof W["with"]]: K extends keyof F[Table]
|
|
218
|
+
? F[Table][K] extends {
|
|
219
|
+
Other: keyof F & keyof Schema;
|
|
220
|
+
type: "referencedBy";
|
|
221
|
+
}
|
|
222
|
+
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
223
|
+
(W["with"][K] extends { with: any }
|
|
224
|
+
? Select<Schema, R, F, F[Table][K]["Other"], W["with"][K]>
|
|
225
|
+
: InferOutput<Schema[F[Table][K]["Other"]]>)[]
|
|
226
|
+
: F[Table][K] extends {
|
|
227
|
+
Other: keyof F & keyof Schema;
|
|
228
|
+
type: "references";
|
|
229
|
+
}
|
|
230
|
+
? | undefined
|
|
231
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
232
|
+
| (W["with"][K] extends { with: any }
|
|
233
|
+
? Select<Schema, R, F, F[Table][K]["Other"], W["with"][K]>
|
|
234
|
+
: InferOutput<Schema[F[Table][K]["Other"]]>)
|
|
235
|
+
: never
|
|
236
|
+
: never;
|
|
237
|
+
}
|
|
238
|
+
: InferOutput<Schema[Table]>;
|
|
239
|
+
|
|
240
|
+
// https://www.geeksforgeeks.org/typescript/union-type-to-intersection-type-in-typescript/
|
|
241
|
+
type UnionToIntersection<U> = (
|
|
242
|
+
U extends object ? (k: U) => void : never
|
|
243
|
+
) extends (k: infer I) => void
|
|
244
|
+
? I
|
|
245
|
+
: never;
|
|
246
|
+
|
|
247
|
+
export function flatten<
|
|
248
|
+
Schema extends Record<string, ObjectSchema<any, any>>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
249
|
+
>(relations: Relations<Schema>): Flatten<Schema>;
|
|
250
|
+
|
|
251
|
+
export function primary<T>(): MetadataAction<T, { primary: true }>;
|
|
252
|
+
|
|
253
|
+
export function references<
|
|
254
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
255
|
+
R extends { entries: any },
|
|
256
|
+
K extends keyof R["entries"],
|
|
257
|
+
T,
|
|
258
|
+
>(
|
|
259
|
+
references: R,
|
|
260
|
+
key: K,
|
|
261
|
+
): MetadataAction<T, { key: R["entries"][K]; references: R }>;
|
|
262
|
+
|
|
263
|
+
export function relations<
|
|
264
|
+
Schema extends Record<string, ObjectSchema<any, any>>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
265
|
+
>(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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
Own.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
Its.
|
|
121
|
-
|
|
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
|
@@ -391,17 +391,35 @@ export function sqlite({ pkg, schema }) {
|
|
|
391
391
|
};
|
|
392
392
|
}
|
|
393
393
|
|
|
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> & { where?: import("./db.d.ts").Where<Schema, Table> }; schema: Schema; table: Table; }) => any[]} */
|
|
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[]} */
|
|
395
395
|
function select(props) {
|
|
396
396
|
ok(typeof props.table == "string");
|
|
397
|
-
let
|
|
398
|
-
|
|
397
|
+
let sql =
|
|
398
|
+
`select * from ${props.table}` +
|
|
399
|
+
whereQuery({ where: props.join?.where }) +
|
|
400
|
+
(props.join?.order
|
|
401
|
+
? "\norder by " +
|
|
402
|
+
(Array.isArray(props.join.order)
|
|
403
|
+
? props.join.order
|
|
404
|
+
: [props.join.order]
|
|
405
|
+
)
|
|
406
|
+
.flatMap((column) =>
|
|
407
|
+
Object.entries(column).map(([name, props]) =>
|
|
408
|
+
props?.desc ? `${name} desc` : name,
|
|
409
|
+
),
|
|
410
|
+
)
|
|
411
|
+
.join(", ")
|
|
412
|
+
: "") +
|
|
413
|
+
(props.join?.offset ? `\noffset ${props.join.offset}` : "") +
|
|
414
|
+
(props.join?.limit ? `\nlimit ${props.join.limit}` : "");
|
|
399
415
|
|
|
416
|
+
let alias = `${props.table}_0`;
|
|
400
417
|
let columns = Object.keys(
|
|
401
418
|
/** @type {any} */ (props.schema)[props.table].entries,
|
|
402
419
|
);
|
|
403
420
|
if (/** @type {any} */ (props.join)?.with) {
|
|
404
421
|
columns = columns.map((name) => `${alias}.${name} ${alias}_${name}`);
|
|
422
|
+
sql = `select COLUMNS\nfrom (${sql}) ${alias}`;
|
|
405
423
|
}
|
|
406
424
|
|
|
407
425
|
/** @type {Record<string, { entities: any[]; table: string }>} */
|
|
@@ -446,11 +464,8 @@ function select(props) {
|
|
|
446
464
|
}
|
|
447
465
|
}
|
|
448
466
|
}
|
|
449
|
-
|
|
450
467
|
join(0, props.table, alias, /** @type {any} */ (props.join));
|
|
451
|
-
sql =
|
|
452
|
-
`select ${columns.join(",\n")} ${sql}` +
|
|
453
|
-
whereQuery({ alias, where: props.join?.where });
|
|
468
|
+
sql = sql.replace("COLUMNS", columns.join(",\n "));
|
|
454
469
|
|
|
455
470
|
const rows = props.database
|
|
456
471
|
.prepare(sql)
|