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.
@@ -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({ where: { id: input.questionId } });
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({ where: { id: input.choiceId } });
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
- where: { id: choice.id },
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
- 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>;
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "evikit",
3
3
  "description": "Preact/SQLite SSR framework for small web apps",
4
- "version": "2.11.1",
4
+ "version": "2.12.0",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": "./index.js",
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({ where }))
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: import("./db.d.ts").Where<typeof schema, typeof table> }} props */
339
- update({ set, where }) {
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({ where }),
347
+ whereQuery(wherePropertyOrSelf(query)),
348
348
  )
349
349
  .run(
350
350
  ...[
351
- ...Object.values(set).map((value) => value ?? null),
352
- ...whereValues(where),
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({ where: props.join?.where }) +
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?.where));
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
- /** @param {{ alias?: string, where: import("./db.js").Where<any, string> | undefined }} props */
547
- function whereQuery({ alias, where }) {
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.values(where || {})
583
- .flatMap((values) =>
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"