@querykitjs/web 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +101 -15
- package/README.md +102 -16
- package/dist/angular/index.cjs +2 -0
- package/dist/angular/index.cjs.map +1 -0
- package/dist/angular/index.d.cts +70 -0
- package/dist/angular/index.d.ts +70 -0
- package/dist/angular/index.js +2 -0
- package/dist/angular/index.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -4
- package/dist/index.d.ts +48 -4
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/{registry-DOVi_zCU.d.cts → registry-CEq4xFui.d.cts} +46 -44
- package/dist/{registry-DOVi_zCU.d.ts → registry-CEq4xFui.d.ts} +46 -44
- package/dist/vue/index.cjs +2 -0
- package/dist/vue/index.cjs.map +1 -0
- package/dist/vue/index.d.cts +64 -0
- package/dist/vue/index.d.ts +64 -0
- package/dist/vue/index.js +2 -0
- package/dist/vue/index.js.map +1 -0
- package/package.json +67 -6
- package/skills/querykit-web/SKILL.md +63 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@querykitjs/web",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Frontend query-building for querykit — typed filters, pagination params & URL sync. Zero-dependency; you send the request.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": {
|
|
@@ -41,12 +41,38 @@
|
|
|
41
41
|
"types": "./dist/react/index.d.cts",
|
|
42
42
|
"default": "./dist/react/index.cjs"
|
|
43
43
|
}
|
|
44
|
+
},
|
|
45
|
+
"./vue": {
|
|
46
|
+
"import": {
|
|
47
|
+
"types": "./dist/vue/index.d.ts",
|
|
48
|
+
"default": "./dist/vue/index.js"
|
|
49
|
+
},
|
|
50
|
+
"require": {
|
|
51
|
+
"types": "./dist/vue/index.d.cts",
|
|
52
|
+
"default": "./dist/vue/index.cjs"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"./angular": {
|
|
56
|
+
"import": {
|
|
57
|
+
"types": "./dist/angular/index.d.ts",
|
|
58
|
+
"default": "./dist/angular/index.js"
|
|
59
|
+
},
|
|
60
|
+
"require": {
|
|
61
|
+
"types": "./dist/angular/index.d.cts",
|
|
62
|
+
"default": "./dist/angular/index.cjs"
|
|
63
|
+
}
|
|
44
64
|
}
|
|
45
65
|
},
|
|
46
66
|
"typesVersions": {
|
|
47
67
|
"*": {
|
|
48
68
|
"react": [
|
|
49
69
|
"./dist/react/index.d.ts"
|
|
70
|
+
],
|
|
71
|
+
"vue": [
|
|
72
|
+
"./dist/vue/index.d.ts"
|
|
73
|
+
],
|
|
74
|
+
"angular": [
|
|
75
|
+
"./dist/angular/index.d.ts"
|
|
50
76
|
]
|
|
51
77
|
}
|
|
52
78
|
},
|
|
@@ -68,15 +94,24 @@
|
|
|
68
94
|
"build": "tsup",
|
|
69
95
|
"dev": "tsup --watch",
|
|
70
96
|
"typecheck": "tsc --noEmit",
|
|
71
|
-
"test:smoke": "bun run test/smoke.ts",
|
|
72
|
-
"test:
|
|
97
|
+
"test:smoke": "bun run test/smoke.ts && bun run test/url-state.ts && bun run test/vue.ts && bun run test/angular.ts && bun run test:real-routers",
|
|
98
|
+
"test:angular": "bun run test/angular.ts",
|
|
99
|
+
"test:real-routers": "bun run test/real-router-react.ts && bun run test/real-router-vue.ts && bun run test/real-router-angular.ts",
|
|
100
|
+
"test:url-state": "bun run test/url-state.ts",
|
|
101
|
+
"test:hook": "bun run test/hook.ts",
|
|
102
|
+
"test:vue": "bun run test/vue.ts"
|
|
73
103
|
},
|
|
74
104
|
"dependencies": {
|
|
75
|
-
"@querykitjs/core": "^2.
|
|
105
|
+
"@querykitjs/core": "^2.1.0"
|
|
76
106
|
},
|
|
77
107
|
"peerDependencies": {
|
|
78
108
|
"react": "^18.3 || ^19.2.7",
|
|
79
|
-
"react-router": "^7.0 || ^8.0"
|
|
109
|
+
"react-router": "^7.0 || ^8.0",
|
|
110
|
+
"vue": "^3.5",
|
|
111
|
+
"vue-router": "^4.6 || ^5",
|
|
112
|
+
"@angular/core": "^21 || ^22",
|
|
113
|
+
"@angular/router": "^21 || ^22",
|
|
114
|
+
"rxjs": "^7.8"
|
|
80
115
|
},
|
|
81
116
|
"peerDependenciesMeta": {
|
|
82
117
|
"react": {
|
|
@@ -84,14 +119,38 @@
|
|
|
84
119
|
},
|
|
85
120
|
"react-router": {
|
|
86
121
|
"optional": true
|
|
122
|
+
},
|
|
123
|
+
"vue": {
|
|
124
|
+
"optional": true
|
|
125
|
+
},
|
|
126
|
+
"vue-router": {
|
|
127
|
+
"optional": true
|
|
128
|
+
},
|
|
129
|
+
"@angular/core": {
|
|
130
|
+
"optional": true
|
|
131
|
+
},
|
|
132
|
+
"@angular/router": {
|
|
133
|
+
"optional": true
|
|
134
|
+
},
|
|
135
|
+
"rxjs": {
|
|
136
|
+
"optional": true
|
|
87
137
|
}
|
|
88
138
|
},
|
|
89
139
|
"devDependencies": {
|
|
140
|
+
"@angular/common": "22.2.4",
|
|
141
|
+
"@angular/compiler": "22.2.4",
|
|
142
|
+
"@angular/core": "22.2.4",
|
|
143
|
+
"@angular/platform-browser": "22.2.4",
|
|
144
|
+
"@angular/router": "22.2.4",
|
|
90
145
|
"@types/react": "19.3.0",
|
|
91
146
|
"@types/react-dom": "19.3.0",
|
|
147
|
+
"happy-dom": "21.0.4",
|
|
92
148
|
"react": "19.3.1",
|
|
93
149
|
"react-dom": "19.3.1",
|
|
94
|
-
"react-router": "8.5.1"
|
|
150
|
+
"react-router": "8.5.1",
|
|
151
|
+
"rxjs": "7.8.2",
|
|
152
|
+
"vue": "3.5.44",
|
|
153
|
+
"vue-router": "5.4.0"
|
|
95
154
|
},
|
|
96
155
|
"keywords": [
|
|
97
156
|
"querykit",
|
|
@@ -102,6 +161,8 @@
|
|
|
102
161
|
"url-state",
|
|
103
162
|
"search-params",
|
|
104
163
|
"react",
|
|
164
|
+
"vue",
|
|
165
|
+
"angular",
|
|
105
166
|
"frontend",
|
|
106
167
|
"typescript"
|
|
107
168
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: querykit-web
|
|
3
|
-
description: Use when frontend code builds or reads a querykit list request (@querykitjs/web,
|
|
3
|
+
description: Use when frontend code builds or reads a querykit list request (@querykitjs/web, /react, /vue, /angular) - createRegistry({ adapter }), qk.resource<T>(name), f filters, sort, list / infinite / cursor payloads, withTotal, parseList / parseInfinite / parseCursor, search, adapter-aware with, defineListSchema and URL helpers (readListParams, setPage, setSort), useListParams / useListParamsBase (clamped) for React and Vue, injectListParams for Angular, createListState, fromSearchParams for SSR, query keys, or reading a 400 with isQueryKitError.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
## Safety rules
|
|
@@ -15,7 +15,7 @@ description: Use when frontend code builds or reads a querykit list request (@qu
|
|
|
15
15
|
|
|
16
16
|
## When to use
|
|
17
17
|
|
|
18
|
-
- A browser or SSR frontend (React, Next.js, any fetch client) that talks to a querykit backend.
|
|
18
|
+
- A browser or SSR frontend (React, Next.js, Vue, Nuxt, Angular, any fetch client) that talks to a querykit backend.
|
|
19
19
|
- Building list, infinite-scroll or cursor request bodies, keeping filters in the URL, and caching with TanStack Query or SWR.
|
|
20
20
|
|
|
21
21
|
The package builds the payload only. You send the request yourself.
|
|
@@ -33,7 +33,7 @@ interface User {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export const qk = createRegistry({
|
|
36
|
-
adapter: "drizzle-pg", // "mongoose" | "drizzle-pg" | "drizzle-sqlite" | "prisma-pg" — types `with` only
|
|
36
|
+
adapter: "drizzle-pg", // "mongoose" | "mongodb" | "drizzle-pg" | "drizzle-sqlite" | "prisma-pg" — types `with` only
|
|
37
37
|
defaults: { perPage: 20, limit: 20 },
|
|
38
38
|
timestamps: { createdAt: "createdAt" }, // same name as the backend; the default sort is this field, desc
|
|
39
39
|
});
|
|
@@ -103,6 +103,11 @@ const posts = createRegistry({ adapter: "drizzle-pg" }).resource<{ id: number; t
|
|
|
103
103
|
// only relations and columns the backend lists in `relations`
|
|
104
104
|
const body = posts.list({ with: { author: { columns: { name: true } } }, page: 1 });
|
|
105
105
|
console.log(body.with);
|
|
106
|
+
|
|
107
|
+
// `@querykitjs/mongodb` loads no relations: `with` is a type error there — fetch the author with a second request
|
|
108
|
+
const comments = createRegistry({ adapter: "mongodb" }).resource<{ _id: string; authorId: string }>("comments");
|
|
109
|
+
const page = comments.list({ page: 1 });
|
|
110
|
+
console.log(page.page);
|
|
106
111
|
```
|
|
107
112
|
|
|
108
113
|
### Filters in the URL
|
|
@@ -146,6 +151,61 @@ export function UsersListPage() {
|
|
|
146
151
|
}
|
|
147
152
|
```
|
|
148
153
|
|
|
154
|
+
### Vue list page with `useListParams`
|
|
155
|
+
|
|
156
|
+
`@querykitjs/web/vue` has the same composables (peers `vue` ^3.5, `vue-router` 4 / 5). `params`, `clamped` and `searchParams` are computed refs; a setter calls `router.push({ query })`.
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
import { createRegistry } from "@querykitjs/web";
|
|
160
|
+
import { useListParams } from "@querykitjs/web/vue";
|
|
161
|
+
import { computed } from "vue";
|
|
162
|
+
|
|
163
|
+
const users = createRegistry({ adapter: "drizzle-pg" }).resource<{ id: number; name: string; status: string }>("users");
|
|
164
|
+
const usersSchema = users.schema({ name: { operation: "%_%", trim: true }, status: { operation: "=" } });
|
|
165
|
+
|
|
166
|
+
// call inside <script setup> or setup()
|
|
167
|
+
export function useUsersList() {
|
|
168
|
+
const { params, clamped, setParam, setPage } = useListParams(users, { schema: usersSchema });
|
|
169
|
+
if (clamped.value) setPage(params.value.page ?? 1); // fix a hand-edited URL
|
|
170
|
+
const queryKey = computed(() => users.keys.list(params.value));
|
|
171
|
+
return { params, queryKey, setName: (name: string) => setParam("name", name) };
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Angular list page with `injectListParams`
|
|
176
|
+
|
|
177
|
+
`@querykitjs/web/angular` has the same state as signals (peers `@angular/core`, `@angular/router` 21 / 22, `rxjs`). A setter calls `router.navigate`. Call it in an injection context, or Angular throws `NG0203`.
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import { computed } from "@angular/core";
|
|
181
|
+
import { createRegistry } from "@querykitjs/web";
|
|
182
|
+
import { injectListParams } from "@querykitjs/web/angular";
|
|
183
|
+
|
|
184
|
+
const users = createRegistry({ adapter: "drizzle-pg" }).resource<{ id: number; name: string; status: string }>("users");
|
|
185
|
+
const usersSchema = users.schema({ name: { operation: "%_%", trim: true }, status: { operation: "=" } });
|
|
186
|
+
|
|
187
|
+
// call from a component field initializer or constructor
|
|
188
|
+
export function injectUsersList() {
|
|
189
|
+
const list = injectListParams(users, { schema: usersSchema });
|
|
190
|
+
const queryKey = computed(() => users.keys.list(list.params()));
|
|
191
|
+
return { list, queryKey, setName: (name: string) => list.setParam("name", name) };
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Any other framework with `createListState`
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import { createListState, createRegistry } from "@querykitjs/web";
|
|
199
|
+
|
|
200
|
+
const users = createRegistry({ adapter: "prisma-pg" }).resource<{ id: number; name: string }>("users");
|
|
201
|
+
const list = createListState(users, { schema: users.schema({ name: { operation: "%_%" } }) });
|
|
202
|
+
|
|
203
|
+
const current = new URLSearchParams("name=ali&page=3");
|
|
204
|
+
const { params } = list.read(current); // the payload to send
|
|
205
|
+
const next = list.actions(current).setSort([{ key: "name", direction: "asc" }]); // new params, page reset
|
|
206
|
+
history.pushState(null, "", `?${next.toString()}`);
|
|
207
|
+
```
|
|
208
|
+
|
|
149
209
|
### SSR payload and a refused request
|
|
150
210
|
|
|
151
211
|
```ts
|