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.
@@ -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/app.js` (uncomment the routes one-by-one when we add them later):
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/app.js"></script>
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/app.js";
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/app.js`.
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/app.js`.
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 Question = db.Question.select({
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>> & { where?: Where<Schema, Table> },
21
- >(
22
- props?: W,
23
- ): Select<Schema, R, F, Table, W>[];
24
-
25
- update(query: {
26
- set: Partial<InferOutput<Schema[Table]>>;
27
- where: Where<Schema, Table>;
28
- }): void;
29
-
30
- upsert(
31
- ...values: InferInput<Schema[Table]>[]
32
- ): InferOutput<Schema[Table]>[];
33
- }
34
- : never;
35
- } & {
36
- schema: Schema;
37
- subscribe(onMessage: ({ table }: { table: string }) => void): () => void;
38
- };
39
-
40
- export type Flatten<
41
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
- Schema extends Record<string, ObjectSchema<any, any>>,
43
- R extends Relations<Schema> = Relations<Schema>,
44
- > = {
45
- [Self in keyof R]: Self extends string
46
- ? UnionToIntersection<
47
- {
48
- [Own in keyof R[Self]]: Own extends string
49
- ? {
50
- [Other in keyof R[Self][Own]]: Other extends string
51
- ? R[Self][Own][Other] extends Record<string, { name: string }>
52
- ? {
53
- [
54
- Its in keyof R[Self][Own][Other] as R[Self][Own][Other][Its]["name"]
55
- ]: Its extends string
56
- ? R[Self][Own][Other][Its]
57
- : never;
58
- }
59
- : never
60
- : never;
61
- }[keyof R[Self][Own]]
62
- : never;
63
- }[keyof R[Self]]
64
- >
65
- : never;
66
- };
67
-
68
- export type Join<Relations, Self extends keyof Relations> = UnionToIntersection<
69
- {
70
- [Own in keyof Relations[Self]]: Own extends string
71
- ? {
72
- [Other in keyof Relations[Self][Own]]: Other extends keyof Relations
73
- ? {
74
- [Its in keyof Relations[Self][Own][Other]]: Its extends string
75
- ? Relations[Self][Own][Other] extends Record<
76
- string,
77
- { name: string }
78
- >
79
- ? {
80
- // Turning this into partial record would cause infinite depth.
81
- // eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style
82
- with: {
83
- [K in Relations[Self][Own][Other][Its]["name"]]?:
84
- Join<Relations, Other> | true;
85
- };
86
- }
87
- : never
88
- : never;
89
- }[keyof Relations[Self][Own][Other]]
90
- : never;
91
- }[keyof Relations[Self][Own]]
92
- : never;
93
- }[keyof Relations[Self]]
94
- >;
95
-
96
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
97
- export type Relations<Schema extends Record<string, ObjectSchema<any, any>>> = {
98
- [Self in keyof Schema]: Self extends string
99
- ? OmitExtendsRecordUndefined<{
100
- [Own in keyof Schema[Self]["entries"]]: Own extends string
101
- ? OmitExtendsRecordUndefined<{
102
- [Other in keyof Schema]: Other extends string
103
- ? OmitExtendsUndefined<{
104
- [Its in keyof Schema[Other]["entries"]]: Its extends string
105
- ? InferMetadata<Schema[Self]["entries"][Own]> extends {
106
- key: Schema[Other]["entries"][Its];
107
- references: Schema[Other];
108
- }
109
- ? Schema[Other] extends InferMetadata<
110
- Schema[Self]["entries"][Own]
111
- >["references"]
112
- ? Schema[Other]["entries"][Its] extends InferMetadata<
113
- Schema[Self]["entries"][Own]
114
- >["key"]
115
- ? {
116
- Its: Its;
117
- name: Rewrite<Self, Own, Other, Its>;
118
- Other: Other;
119
- Own: Own;
120
- type: "references";
121
- }
122
- : undefined
123
- : undefined
124
- : InferMetadata<Schema[Other]["entries"][Its]> extends {
125
- key: Schema[Self]["entries"][Own];
126
- references: Schema[Self];
127
- }
128
- ? Schema[Self] extends InferMetadata<
129
- Schema[Other]["entries"][Its]
130
- >["references"]
131
- ? Schema[Self]["entries"][Own] extends InferMetadata<
132
- Schema[Other]["entries"][Its]
133
- >["key"]
134
- ? {
135
- Its: Its;
136
- name: Rewrite<Self, Own, Other, Its>;
137
- Other: Other;
138
- Own: Own;
139
- type: "referencedBy";
140
- }
141
- : undefined
142
- : undefined
143
- : undefined
144
- : undefined;
145
- }>
146
- : undefined;
147
- }>
148
- : undefined;
149
- }>
150
- : undefined;
151
- };
152
-
153
- export type Where<
154
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
155
- Schema extends Record<string, ObjectSchema<any, any>>,
156
- Table extends keyof Schema,
157
- > = {
158
- [K in keyof Schema[Table]["entries"]]?:
159
- | InferOutput<Schema[Table]["entries"][K]>
160
- | InferOutput<Schema[Table]["entries"][K]>[]
161
- | Partial<
162
- Record<
163
- "gt" | "gte" | "lt" | "lte" | "not",
164
- InferOutput<Schema[Table]["entries"][K]>
165
- >
166
- >;
167
- };
168
-
169
- type OmitExtends<T, U> = {
170
- [K in keyof T as T[K] extends U ? never : K]: T[K];
171
- };
172
-
173
- type OmitExtendsRecordUndefined<T> = OmitExtends<T, Record<string, undefined>>;
174
-
175
- type OmitExtendsUndefined<T> = OmitExtends<T, undefined>;
176
-
177
- type Rewrite<
178
- Self extends string,
179
- Own extends string,
180
- Other extends string,
181
- Its extends string,
182
- > =
183
- // Image.id, Question.imageId -> Image.Question.
184
- Its extends `${Uncapitalize<Self>}${Capitalize<Own>}`
185
- ? Other
186
- : // Question.quizId, Quiz.id -> Question.Quiz.
187
- Own extends `${Uncapitalize<Other>}${Capitalize<Its>}`
188
- ? Other
189
- : // Quiz.coverId, Image.id -> Quiz.cover.
190
- Own extends `${infer R}${Capitalize<Its>}`
191
- ? R
192
- : // Image.id, Quiz.coverId -> Image.CoverQuiz.
193
- Its extends `${infer R}${Capitalize<Own>}`
194
- ? `${Capitalize<R>}${Other}`
195
- : never;
196
-
197
- type Select<
198
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
199
- Schema extends Record<string, ObjectSchema<any, any>>,
200
- R extends Relations<Schema> = Relations<Schema>,
201
- F extends Flatten<Schema> = Flatten<Schema>,
202
- Table extends keyof F & keyof Schema = keyof F & keyof Schema,
203
- W extends Join<R, Table> = Join<R, Table>,
204
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
205
- > = W extends { with: any }
206
- ? InferOutput<Schema[Table]> & {
207
- [K in keyof W["with"]]: K extends keyof F[Table]
208
- ? F[Table][K] extends {
209
- Other: keyof F & keyof Schema;
210
- type: "referencedBy";
211
- }
212
- ? // eslint-disable-next-line @typescript-eslint/no-explicit-any
213
- (W["with"][K] extends { with: any }
214
- ? Select<Schema, R, F, F[Table][K]["Other"], W["with"][K]>
215
- : InferOutput<Schema[F[Table][K]["Other"]]>)[]
216
- : F[Table][K] extends {
217
- Other: keyof F & keyof Schema;
218
- type: "references";
219
- }
220
- ? | undefined
221
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
222
- | (W["with"][K] extends { with: any }
223
- ? Select<Schema, R, F, F[Table][K]["Other"], W["with"][K]>
224
- : InferOutput<Schema[F[Table][K]["Other"]]>)
225
- : never
226
- : never;
227
- }
228
- : InferOutput<Schema[Table]>;
229
-
230
- // https://www.geeksforgeeks.org/typescript/union-type-to-intersection-type-in-typescript/
231
- type UnionToIntersection<U> = (
232
- U extends object ? (k: U) => void : never
233
- ) extends (k: infer I) => void
234
- ? I
235
- : never;
236
-
237
- export function flatten<
238
- Schema extends Record<string, ObjectSchema<any, any>>, // eslint-disable-line @typescript-eslint/no-explicit-any
239
- >(relations: Relations<Schema>): Flatten<Schema>;
240
-
241
- export function primary<T>(): MetadataAction<T, { primary: true }>;
242
-
243
- export function references<
244
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
245
- R extends { entries: any },
246
- K extends keyof R["entries"],
247
- T,
248
- >(
249
- references: R,
250
- key: K,
251
- ): MetadataAction<T, { key: R["entries"][K]; references: R }>;
252
-
253
- export function relations<
254
- Schema extends Record<string, ObjectSchema<any, any>>, // eslint-disable-line @typescript-eslint/no-explicit-any
255
- >(schema: Schema): Relations<Schema>;
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 Its == `${uncapitalize(Self)}${capitalize(Own)}`
112
- ? Other
113
- : // Question.quizId, Quiz.id -> Question.Quiz.
114
- Own == `${uncapitalize(Other)}${capitalize(Its)}`
115
- ? Other
116
- : // Quiz.coverId, Image.id -> Quiz.cover.
117
- Own.endsWith(capitalize(Its))
118
- ? Own.slice(0, -Its.length)
119
- : // Image.id, Quiz.coverId -> Image.CoverQuiz.
120
- Its.endsWith(capitalize(Own))
121
- ? `${capitalize(Its.slice(0, -Own.length))}${Other}`
122
- : undefined;
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "evikit",
3
3
  "description": "Preact/SQLite SSR framework for small web apps",
4
- "version": "2.10.1",
4
+ "version": "2.11.1",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": "./index.js",
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 alias = `${props.table}_0`;
398
- let sql = `from ${props.table} ${alias}`;
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)