evikit 2.8.1 → 2.9.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/sqlite.js CHANGED
@@ -302,16 +302,6 @@ export function sqlite({ pkg, schema }) {
302
302
  const table = /** @type {keyof typeof schema & string} */ (
303
303
  maybeTable
304
304
  );
305
-
306
- /** @param {{ [K in keyof (typeof schema)[typeof table]["entries"]]?: import("valibot").InferOutput<(typeof schema)[typeof table]["entries"][K]> | import("valibot").InferOutput<(typeof schema)[typeof table]["entries"][K]>[]; }} where */
307
- function ensureArrayRecord(where) {
308
- for (const key of Object.keys(where)) {
309
- if (!Array.isArray(where[key])) {
310
- Object.assign(where, { [key]: [where[key]] });
311
- }
312
- }
313
- }
314
-
315
305
  ok(schema[table]);
316
306
  const columns = Object.entries(schema[table].entries);
317
307
  const primaryKey = columns.find(
@@ -329,25 +319,12 @@ export function sqlite({ pkg, schema }) {
329
319
  return [
330
320
  table,
331
321
  {
332
- /** @param {{ [K in keyof (typeof schema)[typeof table]["entries"]]?: import("valibot").InferOutput<(typeof schema)[typeof table]["entries"][K]> | import("valibot").InferOutput<(typeof schema)[typeof table]["entries"][K]>[]; }} where */
322
+ /** @param {import("./db.d.ts").Where<typeof schema, typeof table>} where */
333
323
  delete(where) {
334
- ensureArrayRecord(where);
335
324
  if (
336
325
  database
337
- .prepare(
338
- `delete from ${table} where true\n` +
339
- Object.entries(where)
340
- .map(
341
- ([name, values]) =>
342
- `and ${name} in (${values.map(() => "?").join(", ")})`,
343
- )
344
- .join("\n"),
345
- )
346
- .run(
347
- ...Object.values(where)
348
- .flatMap((values) => values)
349
- .map((value) => value ?? null),
350
- ).changes
326
+ .prepare(`delete from ${table}` + whereQuery({ where }))
327
+ .run(...whereValues(where)).changes
351
328
  ) {
352
329
  publish({ table });
353
330
  }
@@ -355,15 +332,11 @@ export function sqlite({ pkg, schema }) {
355
332
 
356
333
  /** @param {any} join */
357
334
  select(join) {
358
- if (join?.where) {
359
- ensureArrayRecord(join.where);
360
- }
361
335
  return select({ database, flat, join, schema, table });
362
336
  },
363
337
 
364
- /** @param {{ set: Partial<import("valibot").InferOutput<(typeof schema)[typeof table]>>, where: { [K in keyof (typeof schema)[typeof table]["entries"]]?: import("valibot").InferOutput<(typeof schema)[typeof table]["entries"][K]> | import("valibot").InferOutput<(typeof schema)[typeof table]["entries"][K]>[]; } }} props */
338
+ /** @param {{ set: Partial<import("valibot").InferOutput<(typeof schema)[typeof table]>>, where: import("./db.d.ts").Where<typeof schema, typeof table> }} props */
365
339
  update({ set, where }) {
366
- ensureArrayRecord(where);
367
340
  if (
368
341
  database
369
342
  .prepare(
@@ -371,20 +344,12 @@ export function sqlite({ pkg, schema }) {
371
344
  Object.keys(set)
372
345
  .map((name) => `${name} = ?`)
373
346
  .join(",\n") +
374
- `\nwhere true\n` +
375
- Object.entries(where)
376
- .map(
377
- ([name, values]) =>
378
- `and ${name} in (${values.map(() => "?").join(", ")})`,
379
- )
380
- .join("\n"),
347
+ whereQuery({ where }),
381
348
  )
382
349
  .run(
383
350
  ...[
384
351
  ...Object.values(set).map((value) => value ?? null),
385
- ...Object.values(where)
386
- .flatMap((values) => values)
387
- .map((value) => value ?? null),
352
+ ...whereValues(where),
388
353
  ],
389
354
  ).changes
390
355
  ) {
@@ -426,7 +391,7 @@ export function sqlite({ pkg, schema }) {
426
391
  };
427
392
  }
428
393
 
429
- /** @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?: { [K in keyof Schema[Table]["entries"]]?: import("valibot").InferOutput<Schema[Table]["entries"][K]> | import("valibot").InferOutput<Schema[Table]["entries"][K]>[]; } }; 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> & { where?: import("./db.d.ts").Where<Schema, Table> }; schema: Schema; table: Table; }) => any[]} */
430
395
  function select(props) {
431
396
  ok(typeof props.table == "string");
432
397
  let alias = `${props.table}_0`;
@@ -484,19 +449,12 @@ function select(props) {
484
449
 
485
450
  join(0, props.table, alias, /** @type {any} */ (props.join));
486
451
  sql =
487
- `select ${columns.join(",\n")} ${sql}\nwhere true\n` +
488
- Object.entries(/** @type {any} */ (props.join)?.where || {})
489
- .map(
490
- ([name, values]) =>
491
- `and ${alias}.${name} in (${values.map(() => "?").join(", ")})`,
492
- )
493
- .join("\n");
452
+ `select ${columns.join(",\n")} ${sql}` +
453
+ whereQuery({ alias, where: props.join?.where });
494
454
 
495
- const rows = props.database.prepare(sql).all(
496
- ...Object.values(/** @type {any} */ (props.join)?.where || {})
497
- .flatMap((values) => values)
498
- .map((value) => value ?? null),
499
- );
455
+ const rows = props.database
456
+ .prepare(sql)
457
+ .all(...whereValues(props.join?.where));
500
458
  if (!(/** @type {any} */ (props.join)?.with)) {
501
459
  for (const row of rows) {
502
460
  for (const name of Object.keys(row)) {
@@ -569,3 +527,50 @@ function select(props) {
569
527
  ok(state);
570
528
  return state.entities;
571
529
  }
530
+
531
+ /** @param {{ alias?: string, where: import("./db.js").Where<any, string> | undefined }} props */
532
+ function whereQuery({ alias, where }) {
533
+ return (
534
+ "\nwhere true\n" +
535
+ Object.entries(where || {})
536
+ .map(([name, values]) => {
537
+ if (alias) {
538
+ name = `${alias}.${name}`;
539
+ }
540
+ return Array.isArray(values)
541
+ ? `and ${name} in (${values.map(() => "?").join(", ")})`
542
+ : typeof values == "number" || typeof values == "string"
543
+ ? `and ${name} = ?`
544
+ : Object.keys(values)
545
+ .map(
546
+ (operator) =>
547
+ `and ${name} ` +
548
+ (operator == "gt"
549
+ ? ">"
550
+ : operator == "gte"
551
+ ? ">="
552
+ : operator == "lt"
553
+ ? "<"
554
+ : operator == "lte"
555
+ ? "<="
556
+ : "!=") +
557
+ " ?",
558
+ )
559
+ .join("");
560
+ })
561
+ .join("\n")
562
+ );
563
+ }
564
+
565
+ /** @param {import("./db.js").Where<any, string> | undefined} where */
566
+ function whereValues(where) {
567
+ return Object.values(where || {})
568
+ .flatMap((values) =>
569
+ Array.isArray(values)
570
+ ? values
571
+ : typeof values == "number" || typeof values == "string"
572
+ ? values
573
+ : Object.values(values),
574
+ )
575
+ .map((value) => value ?? null);
576
+ }
package/use-api.d.ts CHANGED
@@ -1,13 +1,13 @@
1
- import { type InferOutput, type ObjectSchema } from "valibot";
2
-
3
- export function queryApi<T, U extends ObjectSchema<T, undefined>>(props: {
4
- cookie?: string | undefined;
5
- lang?: string;
6
- origin: string | undefined;
7
- path: string;
8
- schema?: U;
9
- }): { queryFn: () => Promise<InferOutput<U>>; queryKey: string[] };
10
-
11
- export function useApi<T, U extends ObjectSchema<T, undefined>>(routeType: {
12
- outputs: { 200: U };
13
- }): { data?: InferOutput<U>; error?: unknown };
1
+ import { type InferOutput, type ObjectSchema } from "valibot";
2
+
3
+ export function queryApi<T, U extends ObjectSchema<T, undefined>>(props: {
4
+ cookie?: string | undefined;
5
+ lang?: string;
6
+ origin: string | undefined;
7
+ path: string;
8
+ schema?: U;
9
+ }): { queryFn: () => Promise<InferOutput<U>>; queryKey: string[] };
10
+
11
+ export function useApi<T, U extends ObjectSchema<T, undefined>>(routeType: {
12
+ outputs: { 200: U };
13
+ }): { data?: InferOutput<U>; error?: unknown };
package/use-api.js CHANGED
@@ -1,119 +1,119 @@
1
- import { useRoute } from "preact-iso";
2
- import { useEffect, useState } from "preact/hooks";
3
- import { parse } from "valibot";
4
-
5
- import { jsdomCookie } from "./jsdom-cookie.js";
6
- import { SetQueryState, useQuery, useQueryClient } from "./query.js";
7
-
8
- /** @param {{ cookie?: string | undefined, lang?: string, origin: string | undefined, path: string, schema?: any }} props */
9
- export function queryApi({ cookie, lang, origin, path, schema }) {
10
- const queryKey =
11
- path == "/" ? ["home"] : path.split("/").slice(1).map(decodeURIComponent);
12
-
13
- return {
14
- queryFn: async () => {
15
- const response = await fetch(ApiPath({ origin, path }), {
16
- headers: {
17
- ...(lang ? { "Accept-Language": lang } : {}),
18
- Cookie: [...(cookie ? [cookie] : []), ...jsdomCookie.setCookie].join(
19
- "; ",
20
- ),
21
- },
22
- });
23
- if (response.status >= 400) {
24
- const error = new Error(await response.text());
25
- error.name = response.status.toString();
26
- throw Error;
27
- }
28
- const json = await response.json();
29
- return schema ? parse(schema, json) : json;
30
- },
31
- queryKey,
32
- };
33
- }
34
-
35
- /** @param {{ outputs: { 200: any } }} routeType */
36
- export function useApi(routeType) {
37
- const { origin } = location;
38
- const { path: pathWithoutSearch, query } = useRoute();
39
- const search = new URLSearchParams(query).toString();
40
- const path = pathWithoutSearch + (search ? `?${search}` : "");
41
- const schema = routeType.outputs[200];
42
- const { queryFn, queryKey } = queryApi({ origin, path, schema });
43
- const { data, error } = useQuery({ queryFn, queryKey });
44
- const queryClient = useQueryClient();
45
- const setQueryState = SetQueryState({ queryClient, queryKey });
46
- const [sourceUpdatedAt, setSourceUpdatedAt] = useState(Date.now());
47
- const [cookieChangedAt, setCookieChangedAt] = useState(Date.now());
48
- useEffect(() => {
49
- if (typeof cookieStore == "undefined") {
50
- return;
51
- }
52
- function onCookie() {
53
- queryClient.invalidateQueries({ evenIfEventSource: true, queryKey });
54
- setCookieChangedAt(Date.now());
55
- }
56
- cookieStore.addEventListener("change", onCookie);
57
- return () => cookieStore.removeEventListener("change", onCookie);
58
- }, []);
59
- useEffect(() => {
60
- if (typeof EventSource == "undefined") {
61
- return;
62
- }
63
- const source = new EventSource(ApiPath({ origin, path: path }));
64
- source.onerror = () => {
65
- setTimeout(() => {
66
- setSourceUpdatedAt(Date.now());
67
- }, 1e3);
68
- };
69
- source.onmessage = ({ data }) => {
70
- data = JSON.parse(data);
71
- if (!data) {
72
- source.close();
73
- setQueryState((existing) =>
74
- existing ? { ...existing, fromEventSource: false } : undefined,
75
- );
76
- } else if (Number(data.statusCode) >= 400) {
77
- setQueryState((existing) => ({
78
- data: existing?.data ?? null,
79
- dataUpdatedAt: Date.now(),
80
- error,
81
- fromEventSource: true,
82
- isInvalidated: false,
83
- /** @type {"error"} */
84
- status: "error",
85
- }));
86
- } else {
87
- setQueryState(() => ({
88
- data: schema
89
- ? parse(schema, JSON.parse(data.body))
90
- : JSON.parse(data.body),
91
- dataUpdatedAt: Date.now(),
92
- error: null,
93
- fromEventSource: true,
94
- isInvalidated: false,
95
- /** @type {"success"} */
96
- status: "success",
97
- }));
98
- }
99
- };
100
- setQueryState((existing) =>
101
- existing ? { ...existing, fromEventSource: true } : undefined,
102
- );
103
- return () => {
104
- source.close();
105
- setQueryState((existing) =>
106
- existing ? { ...existing, fromEventSource: false } : undefined,
107
- );
108
- };
109
- }, [cookieChangedAt, path, sourceUpdatedAt]);
110
- return { data, error };
111
- }
112
-
113
- /** @param {{ origin: string | undefined, path: string }} props */
114
- function ApiPath({ origin, path }) {
115
- return (
116
- (origin || "") +
117
- path.replace(/^\//, "/api/").replace(/^\/api\/(\?|$)/, "/api$1")
118
- );
119
- }
1
+ import { useRoute } from "preact-iso";
2
+ import { useEffect, useState } from "preact/hooks";
3
+ import { parse } from "valibot";
4
+
5
+ import { jsdomCookie } from "./jsdom-cookie.js";
6
+ import { SetQueryState, useQuery, useQueryClient } from "./query.js";
7
+
8
+ /** @param {{ cookie?: string | undefined, lang?: string, origin: string | undefined, path: string, schema?: any }} props */
9
+ export function queryApi({ cookie, lang, origin, path, schema }) {
10
+ const queryKey =
11
+ path == "/" ? ["home"] : path.split("/").slice(1).map(decodeURIComponent);
12
+
13
+ return {
14
+ queryFn: async () => {
15
+ const response = await fetch(ApiPath({ origin, path }), {
16
+ headers: {
17
+ ...(lang ? { "Accept-Language": lang } : {}),
18
+ Cookie: [...(cookie ? [cookie] : []), ...jsdomCookie.setCookie].join(
19
+ "; ",
20
+ ),
21
+ },
22
+ });
23
+ if (response.status >= 400) {
24
+ const error = new Error(await response.text());
25
+ error.name = response.status.toString();
26
+ throw Error;
27
+ }
28
+ const json = await response.json();
29
+ return schema ? parse(schema, json) : json;
30
+ },
31
+ queryKey,
32
+ };
33
+ }
34
+
35
+ /** @param {{ outputs: { 200: any } }} routeType */
36
+ export function useApi(routeType) {
37
+ const { origin } = location;
38
+ const { path: pathWithoutSearch, query } = useRoute();
39
+ const search = new URLSearchParams(query).toString();
40
+ const path = pathWithoutSearch + (search ? `?${search}` : "");
41
+ const schema = routeType.outputs[200];
42
+ const { queryFn, queryKey } = queryApi({ origin, path, schema });
43
+ const { data, error } = useQuery({ queryFn, queryKey });
44
+ const queryClient = useQueryClient();
45
+ const setQueryState = SetQueryState({ queryClient, queryKey });
46
+ const [sourceUpdatedAt, setSourceUpdatedAt] = useState(Date.now());
47
+ const [cookieChangedAt, setCookieChangedAt] = useState(Date.now());
48
+ useEffect(() => {
49
+ if (typeof cookieStore == "undefined") {
50
+ return;
51
+ }
52
+ function onCookie() {
53
+ queryClient.invalidateQueries({ evenIfEventSource: true, queryKey });
54
+ setCookieChangedAt(Date.now());
55
+ }
56
+ cookieStore.addEventListener("change", onCookie);
57
+ return () => cookieStore.removeEventListener("change", onCookie);
58
+ }, []);
59
+ useEffect(() => {
60
+ if (typeof EventSource == "undefined") {
61
+ return;
62
+ }
63
+ const source = new EventSource(ApiPath({ origin, path: path }));
64
+ source.onerror = () => {
65
+ setTimeout(() => {
66
+ setSourceUpdatedAt(Date.now());
67
+ }, 1e3);
68
+ };
69
+ source.onmessage = ({ data }) => {
70
+ data = JSON.parse(data);
71
+ if (!data) {
72
+ source.close();
73
+ setQueryState((existing) =>
74
+ existing ? { ...existing, fromEventSource: false } : undefined,
75
+ );
76
+ } else if (Number(data.statusCode) >= 400) {
77
+ setQueryState((existing) => ({
78
+ data: existing?.data ?? null,
79
+ dataUpdatedAt: Date.now(),
80
+ error,
81
+ fromEventSource: true,
82
+ isInvalidated: false,
83
+ /** @type {"error"} */
84
+ status: "error",
85
+ }));
86
+ } else {
87
+ setQueryState(() => ({
88
+ data: schema
89
+ ? parse(schema, JSON.parse(data.body))
90
+ : JSON.parse(data.body),
91
+ dataUpdatedAt: Date.now(),
92
+ error: null,
93
+ fromEventSource: true,
94
+ isInvalidated: false,
95
+ /** @type {"success"} */
96
+ status: "success",
97
+ }));
98
+ }
99
+ };
100
+ setQueryState((existing) =>
101
+ existing ? { ...existing, fromEventSource: true } : undefined,
102
+ );
103
+ return () => {
104
+ source.close();
105
+ setQueryState((existing) =>
106
+ existing ? { ...existing, fromEventSource: false } : undefined,
107
+ );
108
+ };
109
+ }, [cookieChangedAt, path, sourceUpdatedAt]);
110
+ return { data, error };
111
+ }
112
+
113
+ /** @param {{ origin: string | undefined, path: string }} props */
114
+ function ApiPath({ origin, path }) {
115
+ return (
116
+ (origin || "") +
117
+ path.replace(/^\//, "/api/").replace(/^\/api\/(\?|$)/, "/api$1")
118
+ );
119
+ }