@mpen/routekit 0.1.0 → 0.1.2
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/dist/bin.d.mts +4 -0
- package/dist/client/react.d.mts +178 -0
- package/dist/client/react.mjs +142 -0
- package/dist/client.d.mts +433 -0
- package/dist/client.mjs +264 -0
- package/dist/content-BuDOmhH_.mjs +102 -0
- package/dist/core-CzUCxvGk.d.mts +140 -0
- package/dist/core-DbmQauwS.mjs +81 -0
- package/dist/handlers.d.mts +72 -0
- package/dist/handlers.mjs +153 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +1152 -0
- package/dist/middleware.d.mts +388 -0
- package/dist/middleware.mjs +1222 -0
- package/dist/request-Dn0zc-xm.mjs +1025 -0
- package/dist/response/content.d.mts +79 -0
- package/dist/response/content.mjs +2 -0
- package/dist/response/json-rpc.d.mts +1 -0
- package/dist/response/json-rpc.mjs +1 -0
- package/dist/response/problem/valibot.d.mts +230 -0
- package/dist/response/problem/valibot.mjs +258 -0
- package/dist/response/problem.d.mts +415 -0
- package/dist/response/problem.mjs +183 -0
- package/dist/response/status.d.mts +45 -0
- package/dist/response/status.mjs +2 -0
- package/dist/responses-B379Ep9Y.d.mts +296 -0
- package/dist/responses-BpVrgeYi.mjs +101 -0
- package/dist/router-Cwb7ak0J.d.mts +1819 -0
- package/dist/routes.d.mts +282 -0
- package/dist/routes.mjs +311 -0
- package/dist/status-C-8mw-FB.mjs +59 -0
- package/dist/valibot-D7liFYyB.d.mts +290 -0
- package/dist/valibot-Du97X-TS.mjs +326 -0
- package/package.json +8 -2
- package/src/bin/gen-api-client.test.ts +0 -70
- package/src/bin/gen-api-client.ts +0 -986
- package/src/client/headers.ts +0 -31
- package/src/client/index.ts +0 -8
- package/src/client/promise.ts +0 -11
- package/src/client/react/index.test.tsx +0 -266
- package/src/client/react/index.ts +0 -431
- package/src/client/responses.test.ts +0 -151
- package/src/client/responses.ts +0 -278
- package/src/client/transport.ts +0 -74
- package/src/client/transports/body-codec.ts +0 -61
- package/src/client/transports/fetch.ts +0 -113
- package/src/client/tsconfig.json +0 -9
- package/src/client/types.ts +0 -15
- package/src/client/url.ts +0 -31
- package/src/index.ts +0 -63
- package/src/router/fetch-types.ts +0 -13
- package/src/router/handlers/index.ts +0 -2
- package/src/router/handlers/openapi/index.ts +0 -2
- package/src/router/handlers/openapi/openapi.ts +0 -293
- package/src/router/integration/zod-openapi.test.ts +0 -74
- package/src/router/lib/charset.test.ts +0 -22
- package/src/router/lib/charset.ts +0 -133
- package/src/router/lib/collections.ts +0 -3
- package/src/router/lib/format.test.ts +0 -67
- package/src/router/lib/format.ts +0 -35
- package/src/router/lib/host.ts +0 -4
- package/src/router/lib/json-schema.ts +0 -6
- package/src/router/lib/media-type.test.ts +0 -122
- package/src/router/lib/media-type.ts +0 -289
- package/src/router/lib/pathname.test.ts +0 -18
- package/src/router/lib/pathname.ts +0 -19
- package/src/router/lib/route-names.ts +0 -70
- package/src/router/lib/route-normalize.test.ts +0 -36
- package/src/router/lib/route-normalize.ts +0 -67
- package/src/router/lib/schema-merge.ts +0 -56
- package/src/router/middleware/accept-ctx.test.ts +0 -33
- package/src/router/middleware/accept-ctx.ts +0 -12
- package/src/router/middleware/body-limit.test.ts +0 -112
- package/src/router/middleware/body-limit.ts +0 -121
- package/src/router/middleware/content-type-context.ts +0 -0
- package/src/router/middleware/cors.test.ts +0 -269
- package/src/router/middleware/cors.ts +0 -490
- package/src/router/middleware/csrf.test.ts +0 -106
- package/src/router/middleware/csrf.ts +0 -192
- package/src/router/middleware/define.ts +0 -249
- package/src/router/middleware/index.ts +0 -34
- package/src/router/middleware/jsxhtml-response.ts +0 -0
- package/src/router/middleware/oas-swagger.ts +0 -0
- package/src/router/middleware/rate-limit.test.ts +0 -886
- package/src/router/middleware/rate-limit.ts +0 -920
- package/src/router/middleware/request-id-ctx.test.ts +0 -183
- package/src/router/middleware/request-id-ctx.ts +0 -135
- package/src/router/middleware/request-logger-format.test.ts +0 -16
- package/src/router/middleware/request-logger-format.ts +0 -269
- package/src/router/middleware/request-logger.test.ts +0 -267
- package/src/router/middleware/request-logger.ts +0 -131
- package/src/router/middleware/start-time-ctx.ts +0 -5
- package/src/router/request.ts +0 -611
- package/src/router/response/core.ts +0 -181
- package/src/router/response/directives.ts +0 -233
- package/src/router/response/formats/content/bodyless.ts +0 -54
- package/src/router/response/formats/content/content.ts +0 -79
- package/src/router/response/formats/content/index.ts +0 -2
- package/src/router/response/formats/json-rpc/index.ts +0 -2
- package/src/router/response/formats/problem/badRequest.ts +0 -90
- package/src/router/response/formats/problem/conflict.ts +0 -90
- package/src/router/response/formats/problem/created.ts +0 -40
- package/src/router/response/formats/problem/index.ts +0 -27
- package/src/router/response/formats/problem/notFound.ts +0 -90
- package/src/router/response/formats/problem/permissionDenied.ts +0 -90
- package/src/router/response/formats/problem/problem.test.ts +0 -888
- package/src/router/response/formats/problem/rateLimited.ts +0 -90
- package/src/router/response/formats/problem/responses.ts +0 -219
- package/src/router/response/formats/problem/root-errors.ts +0 -48
- package/src/router/response/formats/problem/sessionExpired.ts +0 -90
- package/src/router/response/formats/problem/types.ts +0 -170
- package/src/router/response/formats/problem/unauthenticated.ts +0 -90
- package/src/router/response/formats/problem/valibot.ts +0 -410
- package/src/router/response/formats/status/index.ts +0 -1
- package/src/router/response/formats/status/responses.ts +0 -59
- package/src/router/response/formats/status/status.test.ts +0 -21
- package/src/router/response/framers.ts +0 -85
- package/src/router/response/index.ts +0 -28
- package/src/router/response/openapi.test.ts +0 -96
- package/src/router/response/openapi.ts +0 -1
- package/src/router/response/serializers.ts +0 -66
- package/src/router/response/stream.ts +0 -35
- package/src/router/router.test.ts +0 -1571
- package/src/router/router.ts +0 -1965
- package/src/router/routes/index.ts +0 -46
- package/src/router/routes/valibot/index.ts +0 -18
- package/src/router/routes/valibot/valibot.ts +0 -1393
- package/src/router/routes/valibot.test.ts +0 -286
- package/src/router/routes/zod/index.ts +0 -18
- package/src/router/routes/zod/zod.ts +0 -1318
- package/src/router/routes/zod.test.ts +0 -280
- package/src/router/server-interface.ts +0 -31
- package/src/router/types.ts +0 -657
package/src/client/headers.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { MaybePromise } from './promise'
|
|
2
|
-
|
|
3
|
-
type ClientHeadersInit = NonNullable<ConstructorParameters<typeof Headers>[0]>
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Context passed to global header providers.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```ts
|
|
10
|
-
* const headers = (context: ClientHeaderContext) =>
|
|
11
|
-
* context.url.startsWith('/admin') ? { authorization: `Bearer ${token}` } : undefined
|
|
12
|
-
* ```
|
|
13
|
-
*/
|
|
14
|
-
export interface ClientHeaderContext {
|
|
15
|
-
/** The request URL before base URL resolution. */
|
|
16
|
-
url: string
|
|
17
|
-
/** The request init object before execution. */
|
|
18
|
-
init: RequestInit
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Headers or a function that provides headers for each request.
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* ```ts
|
|
26
|
-
* const headers: ClientHeaders = () => ({ authorization: `Bearer ${token}` })
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
export type ClientHeaders =
|
|
30
|
-
| ClientHeadersInit
|
|
31
|
-
| ((context: ClientHeaderContext) => MaybePromise<ClientHeadersInit | undefined>)
|
package/src/client/index.ts
DELETED
package/src/client/promise.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A value that may be returned immediately or through a promise.
|
|
3
|
-
*
|
|
4
|
-
* @example
|
|
5
|
-
* ```ts
|
|
6
|
-
* const headers: MaybePromise<HeadersInit> = { authorization: 'Bearer token' }
|
|
7
|
-
* ```
|
|
8
|
-
*
|
|
9
|
-
* @typeParam T - The resolved value type.
|
|
10
|
-
*/
|
|
11
|
-
export type MaybePromise<T> = T | Promise<T>
|
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, test } from 'bun:test'
|
|
2
|
-
import { expectType, type TypeEqual } from '@mpen/ts-types'
|
|
3
|
-
import { Window } from 'happy-dom'
|
|
4
|
-
import type { ReactNode } from 'react'
|
|
5
|
-
import type { ApiResponse } from '../responses'
|
|
6
|
-
|
|
7
|
-
const testWindow = new Window({ url: 'http://localhost/' })
|
|
8
|
-
|
|
9
|
-
testWindow.SyntaxError = SyntaxError
|
|
10
|
-
|
|
11
|
-
Object.assign(globalThis, {
|
|
12
|
-
IS_REACT_ACT_ENVIRONMENT: true,
|
|
13
|
-
window: testWindow,
|
|
14
|
-
document: testWindow.document,
|
|
15
|
-
navigator: testWindow.navigator,
|
|
16
|
-
HTMLElement: testWindow.HTMLElement,
|
|
17
|
-
SyntaxError,
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
const { act } = await import('react')
|
|
21
|
-
const { createRoot } = await import('react-dom/client')
|
|
22
|
-
const { useQuery } = await import('./index')
|
|
23
|
-
|
|
24
|
-
const roots: Array<ReturnType<typeof createRoot>> = []
|
|
25
|
-
|
|
26
|
-
function render(children: ReactNode) {
|
|
27
|
-
const container = document.createElement('div')
|
|
28
|
-
document.body.append(container)
|
|
29
|
-
|
|
30
|
-
const root = createRoot(container)
|
|
31
|
-
roots.push(root)
|
|
32
|
-
|
|
33
|
-
act(() => {
|
|
34
|
-
root.render(children)
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
return { container, root }
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function text(container: Element, testId: string): string | undefined {
|
|
41
|
-
return container.querySelector(`[data-testid="${testId}"]`)?.textContent ?? undefined
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function waitFrame(): Promise<void> {
|
|
45
|
-
return new Promise((resolve) => setTimeout(resolve, 0))
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async function waitFor(assertion: () => void): Promise<void> {
|
|
49
|
-
let lastError: unknown
|
|
50
|
-
|
|
51
|
-
for (let attempt = 0; attempt < 30; attempt += 1) {
|
|
52
|
-
try {
|
|
53
|
-
assertion()
|
|
54
|
-
return
|
|
55
|
-
} catch (error) {
|
|
56
|
-
lastError = error
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
await act(async () => {
|
|
60
|
-
await waitFrame()
|
|
61
|
-
})
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
throw lastError
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
afterEach(() => {
|
|
68
|
-
for (const root of roots.splice(0)) {
|
|
69
|
-
act(() => {
|
|
70
|
-
root.unmount()
|
|
71
|
-
})
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
document.body.replaceChildren()
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
describe(useQuery.name, () => {
|
|
78
|
-
test('runs endpoint methods and refetches when arguments change', async () => {
|
|
79
|
-
type UserResponse = ApiResponse<{ enabled: boolean; id: number }>
|
|
80
|
-
const calls: Array<{ id: number; request: { body: boolean } }> = []
|
|
81
|
-
const endpoint = async (id: number, request: { body: boolean }): Promise<UserResponse> => {
|
|
82
|
-
calls.push({ id, request })
|
|
83
|
-
return {
|
|
84
|
-
ok: true,
|
|
85
|
-
status: 200,
|
|
86
|
-
headers: new Headers(),
|
|
87
|
-
body: { enabled: request.body, id },
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function Reader({ id }: { id: number }) {
|
|
92
|
-
const query = useQuery(endpoint, id, { body: true })
|
|
93
|
-
|
|
94
|
-
expectType<TypeEqual<typeof query.data, { enabled: boolean; id: number } | undefined>>(
|
|
95
|
-
true,
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
return (
|
|
99
|
-
<span data-testid="state">{`${query.status}:${query.data?.id ?? 'none'}:${String(query.isFetching)}`}</span>
|
|
100
|
-
)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const { container, root } = render(<Reader id={1} />)
|
|
104
|
-
|
|
105
|
-
await waitFor(() => {
|
|
106
|
-
expect(text(container, 'state')).toBe('success:1:false')
|
|
107
|
-
})
|
|
108
|
-
expect(calls).toEqual([{ id: 1, request: { body: true } }])
|
|
109
|
-
|
|
110
|
-
act(() => {
|
|
111
|
-
root.render(<Reader id={1} />)
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
expect(calls).toHaveLength(1)
|
|
115
|
-
|
|
116
|
-
act(() => {
|
|
117
|
-
root.render(<Reader id={2} />)
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
await waitFor(() => {
|
|
121
|
-
expect(text(container, 'state')).toBe('success:2:false')
|
|
122
|
-
})
|
|
123
|
-
expect(calls).toEqual([
|
|
124
|
-
{ id: 1, request: { body: true } },
|
|
125
|
-
{ id: 2, request: { body: true } },
|
|
126
|
-
])
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
test('supports options-first calls, selected data, and manual refetching', async () => {
|
|
130
|
-
type UserResponse = ApiResponse<{ id: number; label: string }>
|
|
131
|
-
const calls: number[] = []
|
|
132
|
-
const endpoint = async (id: number): Promise<UserResponse> => {
|
|
133
|
-
calls.push(id)
|
|
134
|
-
return {
|
|
135
|
-
ok: true,
|
|
136
|
-
status: 200,
|
|
137
|
-
headers: new Headers(),
|
|
138
|
-
body: { id, label: `user-${calls.length}` },
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
function Reader({ enabled }: { enabled: boolean }) {
|
|
142
|
-
const query = useQuery(
|
|
143
|
-
{ enabled, select: (response) => response.body.label },
|
|
144
|
-
endpoint,
|
|
145
|
-
123,
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
expectType<TypeEqual<typeof query.data, string | undefined>>(true)
|
|
149
|
-
|
|
150
|
-
return (
|
|
151
|
-
<>
|
|
152
|
-
<span data-testid="label">{`${query.status}:${query.data ?? 'none'}`}</span>
|
|
153
|
-
<button type="button" onClick={() => void query.refetch()}>
|
|
154
|
-
Refetch
|
|
155
|
-
</button>
|
|
156
|
-
</>
|
|
157
|
-
)
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const { container, root } = render(<Reader enabled={false} />)
|
|
161
|
-
|
|
162
|
-
expect(text(container, 'label')).toBe('idle:none')
|
|
163
|
-
expect(calls).toEqual([])
|
|
164
|
-
|
|
165
|
-
act(() => {
|
|
166
|
-
root.render(<Reader enabled />)
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
await waitFor(() => {
|
|
170
|
-
expect(text(container, 'label')).toBe('success:user-1')
|
|
171
|
-
})
|
|
172
|
-
expect(calls).toEqual([123])
|
|
173
|
-
|
|
174
|
-
act(() => {
|
|
175
|
-
container.querySelector('button')?.click()
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
await waitFor(() => {
|
|
179
|
-
expect(text(container, 'label')).toBe('success:user-2')
|
|
180
|
-
})
|
|
181
|
-
expect(calls).toEqual([123, 123])
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
test('keeps previous data by default while refetching', async () => {
|
|
185
|
-
type UserResponse = ApiResponse<{ id: number }>
|
|
186
|
-
let resolveSecond: ((response: UserResponse) => void) | undefined
|
|
187
|
-
const endpoint = (id: number): Promise<UserResponse> => {
|
|
188
|
-
if (id === 1) {
|
|
189
|
-
return Promise.resolve({
|
|
190
|
-
ok: true,
|
|
191
|
-
status: 200,
|
|
192
|
-
headers: new Headers(),
|
|
193
|
-
body: { id },
|
|
194
|
-
})
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return new Promise((resolve) => {
|
|
198
|
-
resolveSecond = resolve
|
|
199
|
-
})
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function Reader({ id }: { id: number }) {
|
|
203
|
-
const query = useQuery(endpoint, id)
|
|
204
|
-
|
|
205
|
-
return (
|
|
206
|
-
<span data-testid="state">{`${query.status}:${query.data?.id ?? 'none'}:${String(query.isFetching)}`}</span>
|
|
207
|
-
)
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const { container, root } = render(<Reader id={1} />)
|
|
211
|
-
|
|
212
|
-
await waitFor(() => {
|
|
213
|
-
expect(text(container, 'state')).toBe('success:1:false')
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
act(() => {
|
|
217
|
-
root.render(<Reader id={2} />)
|
|
218
|
-
})
|
|
219
|
-
|
|
220
|
-
await waitFor(() => {
|
|
221
|
-
expect(text(container, 'state')).toBe('success:1:true')
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
await act(async () => {
|
|
225
|
-
resolveSecond?.({
|
|
226
|
-
ok: true,
|
|
227
|
-
status: 200,
|
|
228
|
-
headers: new Headers(),
|
|
229
|
-
body: { id: 2 },
|
|
230
|
-
})
|
|
231
|
-
await waitFrame()
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
await waitFor(() => {
|
|
235
|
-
expect(text(container, 'state')).toBe('success:2:false')
|
|
236
|
-
})
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
test('accepts generated-style endpoint properties with required object arguments', async () => {
|
|
240
|
-
type TodoResponse = ApiResponse<{ todos: string[] }>
|
|
241
|
-
const apiClient = {
|
|
242
|
-
todos: {
|
|
243
|
-
list: {
|
|
244
|
-
get: async (query: { filter: 'all' | 'active' }): Promise<TodoResponse> => ({
|
|
245
|
-
ok: true,
|
|
246
|
-
status: 200,
|
|
247
|
-
headers: new Headers(),
|
|
248
|
-
body: { todos: [query.filter] },
|
|
249
|
-
}),
|
|
250
|
-
},
|
|
251
|
-
},
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function Reader() {
|
|
255
|
-
const query = useQuery({ enabled: false }, apiClient.todos.list.get, { filter: 'all' })
|
|
256
|
-
|
|
257
|
-
expectType<TypeEqual<typeof query.data, { todos: string[] } | undefined>>(true)
|
|
258
|
-
|
|
259
|
-
return <span data-testid="state">{query.status}</span>
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const { container } = render(<Reader />)
|
|
263
|
-
|
|
264
|
-
expect(text(container, 'state')).toBe('idle')
|
|
265
|
-
})
|
|
266
|
-
})
|