@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.
Files changed (133) hide show
  1. package/dist/bin.d.mts +4 -0
  2. package/dist/client/react.d.mts +178 -0
  3. package/dist/client/react.mjs +142 -0
  4. package/dist/client.d.mts +433 -0
  5. package/dist/client.mjs +264 -0
  6. package/dist/content-BuDOmhH_.mjs +102 -0
  7. package/dist/core-CzUCxvGk.d.mts +140 -0
  8. package/dist/core-DbmQauwS.mjs +81 -0
  9. package/dist/handlers.d.mts +72 -0
  10. package/dist/handlers.mjs +153 -0
  11. package/dist/index.d.mts +3 -0
  12. package/dist/index.mjs +1152 -0
  13. package/dist/middleware.d.mts +388 -0
  14. package/dist/middleware.mjs +1222 -0
  15. package/dist/request-Dn0zc-xm.mjs +1025 -0
  16. package/dist/response/content.d.mts +79 -0
  17. package/dist/response/content.mjs +2 -0
  18. package/dist/response/json-rpc.d.mts +1 -0
  19. package/dist/response/json-rpc.mjs +1 -0
  20. package/dist/response/problem/valibot.d.mts +230 -0
  21. package/dist/response/problem/valibot.mjs +258 -0
  22. package/dist/response/problem.d.mts +415 -0
  23. package/dist/response/problem.mjs +183 -0
  24. package/dist/response/status.d.mts +45 -0
  25. package/dist/response/status.mjs +2 -0
  26. package/dist/responses-B379Ep9Y.d.mts +296 -0
  27. package/dist/responses-BpVrgeYi.mjs +101 -0
  28. package/dist/router-Cwb7ak0J.d.mts +1819 -0
  29. package/dist/routes.d.mts +282 -0
  30. package/dist/routes.mjs +311 -0
  31. package/dist/status-C-8mw-FB.mjs +59 -0
  32. package/dist/valibot-D7liFYyB.d.mts +290 -0
  33. package/dist/valibot-Du97X-TS.mjs +326 -0
  34. package/package.json +8 -2
  35. package/src/bin/gen-api-client.test.ts +0 -70
  36. package/src/bin/gen-api-client.ts +0 -986
  37. package/src/client/headers.ts +0 -31
  38. package/src/client/index.ts +0 -8
  39. package/src/client/promise.ts +0 -11
  40. package/src/client/react/index.test.tsx +0 -266
  41. package/src/client/react/index.ts +0 -431
  42. package/src/client/responses.test.ts +0 -151
  43. package/src/client/responses.ts +0 -278
  44. package/src/client/transport.ts +0 -74
  45. package/src/client/transports/body-codec.ts +0 -61
  46. package/src/client/transports/fetch.ts +0 -113
  47. package/src/client/tsconfig.json +0 -9
  48. package/src/client/types.ts +0 -15
  49. package/src/client/url.ts +0 -31
  50. package/src/index.ts +0 -63
  51. package/src/router/fetch-types.ts +0 -13
  52. package/src/router/handlers/index.ts +0 -2
  53. package/src/router/handlers/openapi/index.ts +0 -2
  54. package/src/router/handlers/openapi/openapi.ts +0 -293
  55. package/src/router/integration/zod-openapi.test.ts +0 -74
  56. package/src/router/lib/charset.test.ts +0 -22
  57. package/src/router/lib/charset.ts +0 -133
  58. package/src/router/lib/collections.ts +0 -3
  59. package/src/router/lib/format.test.ts +0 -67
  60. package/src/router/lib/format.ts +0 -35
  61. package/src/router/lib/host.ts +0 -4
  62. package/src/router/lib/json-schema.ts +0 -6
  63. package/src/router/lib/media-type.test.ts +0 -122
  64. package/src/router/lib/media-type.ts +0 -289
  65. package/src/router/lib/pathname.test.ts +0 -18
  66. package/src/router/lib/pathname.ts +0 -19
  67. package/src/router/lib/route-names.ts +0 -70
  68. package/src/router/lib/route-normalize.test.ts +0 -36
  69. package/src/router/lib/route-normalize.ts +0 -67
  70. package/src/router/lib/schema-merge.ts +0 -56
  71. package/src/router/middleware/accept-ctx.test.ts +0 -33
  72. package/src/router/middleware/accept-ctx.ts +0 -12
  73. package/src/router/middleware/body-limit.test.ts +0 -112
  74. package/src/router/middleware/body-limit.ts +0 -121
  75. package/src/router/middleware/content-type-context.ts +0 -0
  76. package/src/router/middleware/cors.test.ts +0 -269
  77. package/src/router/middleware/cors.ts +0 -490
  78. package/src/router/middleware/csrf.test.ts +0 -106
  79. package/src/router/middleware/csrf.ts +0 -192
  80. package/src/router/middleware/define.ts +0 -249
  81. package/src/router/middleware/index.ts +0 -34
  82. package/src/router/middleware/jsxhtml-response.ts +0 -0
  83. package/src/router/middleware/oas-swagger.ts +0 -0
  84. package/src/router/middleware/rate-limit.test.ts +0 -886
  85. package/src/router/middleware/rate-limit.ts +0 -920
  86. package/src/router/middleware/request-id-ctx.test.ts +0 -183
  87. package/src/router/middleware/request-id-ctx.ts +0 -135
  88. package/src/router/middleware/request-logger-format.test.ts +0 -16
  89. package/src/router/middleware/request-logger-format.ts +0 -269
  90. package/src/router/middleware/request-logger.test.ts +0 -267
  91. package/src/router/middleware/request-logger.ts +0 -131
  92. package/src/router/middleware/start-time-ctx.ts +0 -5
  93. package/src/router/request.ts +0 -611
  94. package/src/router/response/core.ts +0 -181
  95. package/src/router/response/directives.ts +0 -233
  96. package/src/router/response/formats/content/bodyless.ts +0 -54
  97. package/src/router/response/formats/content/content.ts +0 -79
  98. package/src/router/response/formats/content/index.ts +0 -2
  99. package/src/router/response/formats/json-rpc/index.ts +0 -2
  100. package/src/router/response/formats/problem/badRequest.ts +0 -90
  101. package/src/router/response/formats/problem/conflict.ts +0 -90
  102. package/src/router/response/formats/problem/created.ts +0 -40
  103. package/src/router/response/formats/problem/index.ts +0 -27
  104. package/src/router/response/formats/problem/notFound.ts +0 -90
  105. package/src/router/response/formats/problem/permissionDenied.ts +0 -90
  106. package/src/router/response/formats/problem/problem.test.ts +0 -888
  107. package/src/router/response/formats/problem/rateLimited.ts +0 -90
  108. package/src/router/response/formats/problem/responses.ts +0 -219
  109. package/src/router/response/formats/problem/root-errors.ts +0 -48
  110. package/src/router/response/formats/problem/sessionExpired.ts +0 -90
  111. package/src/router/response/formats/problem/types.ts +0 -170
  112. package/src/router/response/formats/problem/unauthenticated.ts +0 -90
  113. package/src/router/response/formats/problem/valibot.ts +0 -410
  114. package/src/router/response/formats/status/index.ts +0 -1
  115. package/src/router/response/formats/status/responses.ts +0 -59
  116. package/src/router/response/formats/status/status.test.ts +0 -21
  117. package/src/router/response/framers.ts +0 -85
  118. package/src/router/response/index.ts +0 -28
  119. package/src/router/response/openapi.test.ts +0 -96
  120. package/src/router/response/openapi.ts +0 -1
  121. package/src/router/response/serializers.ts +0 -66
  122. package/src/router/response/stream.ts +0 -35
  123. package/src/router/router.test.ts +0 -1571
  124. package/src/router/router.ts +0 -1965
  125. package/src/router/routes/index.ts +0 -46
  126. package/src/router/routes/valibot/index.ts +0 -18
  127. package/src/router/routes/valibot/valibot.ts +0 -1393
  128. package/src/router/routes/valibot.test.ts +0 -286
  129. package/src/router/routes/zod/index.ts +0 -18
  130. package/src/router/routes/zod/zod.ts +0 -1318
  131. package/src/router/routes/zod.test.ts +0 -280
  132. package/src/router/server-interface.ts +0 -31
  133. package/src/router/types.ts +0 -657
package/dist/bin.d.mts ADDED
@@ -0,0 +1,4 @@
1
+ //#region src/bin/gen-api-client.d.ts
2
+ declare function main(): Promise<void>;
3
+ //#endregion
4
+ export { main };
@@ -0,0 +1,178 @@
1
+ //#region src/client/react/index.d.ts
2
+ /**
3
+ * A generated API client endpoint method that can be called by [`useQuery`]{@link useQuery}.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * const method: QueryMethod<[id: number], ApiResponse<{ id: number }>> = apiClient.users.get
8
+ * ```
9
+ *
10
+ * @typeParam TArgs - The endpoint argument tuple.
11
+ * @typeParam TResponse - The resolved endpoint response type.
12
+ */
13
+ type QueryMethod<TArgs extends unknown[] = any[], TResponse = unknown> = (...args: TArgs) => Promise<TResponse>;
14
+ /**
15
+ * Resolves the response type returned by a [`QueryMethod`]{@link QueryMethod}.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * type Response = QueryResponse<typeof apiClient.users.get>
20
+ * ```
21
+ *
22
+ * @typeParam TMethod - The endpoint method type.
23
+ */
24
+ type QueryResponse<TMethod extends QueryMethod> = Awaited<ReturnType<TMethod>>;
25
+ /**
26
+ * Infers the default data value exposed by [`useQuery`]{@link useQuery}.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * type Data = QueryData<ApiResponse<{ ok: true }>>
31
+ * ```
32
+ *
33
+ * @typeParam TResponse - The endpoint response type.
34
+ */
35
+ type QueryData<TResponse> = TResponse extends {
36
+ body: infer TBody;
37
+ } ? TBody : TResponse;
38
+ /**
39
+ * Current lifecycle status for a [`useQuery`]{@link useQuery} request.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * if (query.status === 'success') {
44
+ * query.data
45
+ * }
46
+ * ```
47
+ */
48
+ type UseQueryStatus = 'idle' | 'loading' | 'success' | 'error';
49
+ /**
50
+ * Options for [`useQuery`]{@link useQuery}.
51
+ *
52
+ * @example
53
+ * ```tsx
54
+ * const query = useQuery(
55
+ * { enabled: userId !== undefined, keepPreviousData: true },
56
+ * apiClient.users.get,
57
+ * userId,
58
+ * )
59
+ * ```
60
+ *
61
+ * @typeParam TResponse - The endpoint response type.
62
+ * @typeParam TData - The selected data type exposed on the result.
63
+ */
64
+ interface UseQueryOptions<TResponse, TData = QueryData<TResponse>> {
65
+ /**
66
+ * Whether the hook should automatically run the endpoint.
67
+ */
68
+ enabled?: boolean;
69
+ /**
70
+ * Keeps the previous response and data visible while the next request is loading.
71
+ * @defaultValue true
72
+ */
73
+ keepPreviousData?: boolean;
74
+ /**
75
+ * Optional query key used instead of the endpoint arguments when deciding whether to refetch.
76
+ */
77
+ key?: unknown;
78
+ /**
79
+ * Selects the result data from the endpoint response.
80
+ */
81
+ select?: (response: TResponse) => TData;
82
+ }
83
+ /**
84
+ * Result returned by [`useQuery`]{@link useQuery}.
85
+ *
86
+ * @example
87
+ * ```tsx
88
+ * const query = useQuery(apiClient.health.get)
89
+ *
90
+ * if (query.isLoading) return 'Loading'
91
+ * if (query.isError) return String(query.error)
92
+ *
93
+ * return query.data.ok
94
+ * ```
95
+ *
96
+ * @typeParam TResponse - The endpoint response type.
97
+ * @typeParam TData - The selected data type exposed on the result.
98
+ */
99
+ interface UseQueryResult<TResponse, TData = QueryData<TResponse>> {
100
+ /**
101
+ * The current request lifecycle status.
102
+ */
103
+ status: UseQueryStatus;
104
+ /**
105
+ * The latest selected data. By default this is `response.body`.
106
+ */
107
+ data: TData | undefined;
108
+ /**
109
+ * The latest full endpoint response.
110
+ */
111
+ response: TResponse | undefined;
112
+ /**
113
+ * The latest request error.
114
+ */
115
+ error: unknown;
116
+ /**
117
+ * Whether the query has not run yet.
118
+ */
119
+ isIdle: boolean;
120
+ /**
121
+ * Whether the query is loading without any successful response.
122
+ */
123
+ isLoading: boolean;
124
+ /**
125
+ * Whether a request is currently in flight.
126
+ */
127
+ isFetching: boolean;
128
+ /**
129
+ * Whether the query has a successful response.
130
+ */
131
+ isSuccess: boolean;
132
+ /**
133
+ * Whether the latest request failed.
134
+ */
135
+ isError: boolean;
136
+ /**
137
+ * Runs the endpoint again with the latest method arguments.
138
+ *
139
+ * @returns A promise for the endpoint response.
140
+ */
141
+ refetch(): Promise<TResponse>;
142
+ }
143
+ /**
144
+ * Runs a generated API endpoint method and refetches when its arguments change.
145
+ *
146
+ * @example
147
+ * ```tsx
148
+ * const query = useQuery(apiClient.usersById.get, 123, { includePosts: true })
149
+ * ```
150
+ *
151
+ * @param method - Generated API client endpoint method to run.
152
+ * @param args - Arguments passed to the endpoint method.
153
+ * @returns Query state, the latest response body, the full response, and a manual refetch function.
154
+ * @typeParam TMethod - The endpoint method type.
155
+ */
156
+ declare function useQuery<TMethod extends QueryMethod>(method: TMethod, ...args: Parameters<TMethod>): UseQueryResult<QueryResponse<TMethod>, QueryData<QueryResponse<TMethod>>>;
157
+ /**
158
+ * Runs a generated API endpoint method with query options and refetches when its arguments change.
159
+ *
160
+ * @example
161
+ * ```tsx
162
+ * const query = useQuery(
163
+ * { keepPreviousData: true, select: (response) => response.body.items },
164
+ * apiClient.search.get,
165
+ * { q: 'routekit' },
166
+ * )
167
+ * ```
168
+ *
169
+ * @param options - Query behavior options.
170
+ * @param method - Generated API client endpoint method to run.
171
+ * @param args - Arguments passed to the endpoint method.
172
+ * @returns Query state, the selected data, the full response, and a manual refetch function.
173
+ * @typeParam TMethod - The endpoint method type.
174
+ * @typeParam TData - The selected data type.
175
+ */
176
+ declare function useQuery<TMethod extends QueryMethod, TData = QueryData<QueryResponse<TMethod>>>(options: UseQueryOptions<QueryResponse<TMethod>, TData>, method: TMethod, ...args: Parameters<TMethod>): UseQueryResult<QueryResponse<TMethod>, TData>;
177
+ //#endregion
178
+ export { QueryData, QueryMethod, QueryResponse, UseQueryOptions, UseQueryResult, UseQueryStatus, useQuery };
@@ -0,0 +1,142 @@
1
+ import { useCallback, useDebugValue, useEffect, useRef, useState } from "react";
2
+ //#region src/client/react/index.ts
3
+ const isPlainObject = (value) => Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null;
4
+ const hasBody = (value) => typeof value === "object" && value !== null && "body" in value;
5
+ function defaultSelect(response) {
6
+ return hasBody(response) ? response.body : response;
7
+ }
8
+ function createInitialState(enabled) {
9
+ return {
10
+ status: enabled ? "loading" : "idle",
11
+ data: void 0,
12
+ response: void 0,
13
+ error: void 0,
14
+ isFetching: enabled
15
+ };
16
+ }
17
+ function startQuery(state, keepPreviousData) {
18
+ return {
19
+ status: keepPreviousData && state.response !== void 0 ? state.status : "loading",
20
+ data: keepPreviousData ? state.data : void 0,
21
+ response: keepPreviousData ? state.response : void 0,
22
+ error: void 0,
23
+ isFetching: true
24
+ };
25
+ }
26
+ function stopQuery(state) {
27
+ return {
28
+ ...state,
29
+ status: state.response === void 0 ? "idle" : state.status,
30
+ isFetching: false
31
+ };
32
+ }
33
+ function toQueryResult(state, refetch) {
34
+ return {
35
+ ...state,
36
+ isIdle: state.status === "idle",
37
+ isLoading: state.status === "loading",
38
+ isSuccess: state.status === "success",
39
+ isError: state.status === "error",
40
+ refetch
41
+ };
42
+ }
43
+ function hashQueryKey(value) {
44
+ return stableStringify(value, /* @__PURE__ */ new WeakMap(), { value: 0 });
45
+ }
46
+ function stableStringify(value, seen, nextSeenIndex) {
47
+ if (value === null) return "null";
48
+ switch (typeof value) {
49
+ case "bigint": return `bigint:${value}`;
50
+ case "boolean":
51
+ case "number":
52
+ case "string": return JSON.stringify(value);
53
+ case "symbol": return `symbol:${String(value.description)}`;
54
+ case "undefined": return "undefined";
55
+ case "function": return `function:${String(value)}`;
56
+ }
57
+ const seenIndex = seen.get(value);
58
+ if (seenIndex !== void 0) return `[Circular:${seenIndex}]`;
59
+ seen.set(value, nextSeenIndex.value);
60
+ nextSeenIndex.value += 1;
61
+ if (Array.isArray(value)) return `[${value.map((item) => stableStringify(item, seen, nextSeenIndex)).join(",")}]`;
62
+ if (value instanceof Date) return `Date:${value.toISOString()}`;
63
+ if (typeof URLSearchParams !== "undefined" && value instanceof URLSearchParams) return `URLSearchParams:${stableStringify(Array.from(value.entries()), seen, nextSeenIndex)}`;
64
+ if (typeof Headers !== "undefined" && value instanceof Headers) return `Headers:${stableStringify(Array.from(value.entries()).sort(), seen, nextSeenIndex)}`;
65
+ if (value instanceof Map) return `Map:${stableStringify(Array.from(value.entries()).sort(), seen, nextSeenIndex)}`;
66
+ if (value instanceof Set) return `Set:${stableStringify(Array.from(value.values()).sort(), seen, nextSeenIndex)}`;
67
+ if (!isPlainObject(value)) return Object.prototype.toString.call(value);
68
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(value[key], seen, nextSeenIndex)}`).join(",")}}`;
69
+ }
70
+ function useQuery(...input) {
71
+ const hasOptions = typeof input[0] !== "function";
72
+ const options = hasOptions ? input[0] : void 0;
73
+ const method = hasOptions ? input[1] : input[0];
74
+ const args = input.slice(hasOptions ? 2 : 1);
75
+ const enabled = options?.enabled ?? true;
76
+ const queryKey = hashQueryKey(options?.key ?? args);
77
+ const select = options?.select ?? defaultSelect;
78
+ const argsRef = useRef(args);
79
+ const keepPreviousDataRef = useRef(options?.keepPreviousData ?? true);
80
+ const mountedRef = useRef(false);
81
+ const requestIdRef = useRef(0);
82
+ const selectRef = useRef(select);
83
+ const [state, setState] = useState(() => createInitialState(enabled));
84
+ useEffect(() => {
85
+ argsRef.current = args;
86
+ keepPreviousDataRef.current = options?.keepPreviousData ?? true;
87
+ selectRef.current = select;
88
+ });
89
+ useEffect(() => {
90
+ mountedRef.current = true;
91
+ return () => {
92
+ mountedRef.current = false;
93
+ requestIdRef.current += 1;
94
+ };
95
+ }, []);
96
+ const refetch = useCallback(async () => {
97
+ const requestId = requestIdRef.current + 1;
98
+ requestIdRef.current = requestId;
99
+ setState((currentState) => startQuery(currentState, keepPreviousDataRef.current));
100
+ try {
101
+ const response = await method(...argsRef.current);
102
+ const data = selectRef.current(response);
103
+ if (mountedRef.current && requestId === requestIdRef.current) setState({
104
+ status: "success",
105
+ data,
106
+ response,
107
+ error: void 0,
108
+ isFetching: false
109
+ });
110
+ return response;
111
+ } catch (error) {
112
+ if (mountedRef.current && requestId === requestIdRef.current) setState((currentState) => ({
113
+ status: "error",
114
+ data: keepPreviousDataRef.current ? currentState.data : void 0,
115
+ response: keepPreviousDataRef.current ? currentState.response : void 0,
116
+ error,
117
+ isFetching: false
118
+ }));
119
+ throw error;
120
+ }
121
+ }, [method]);
122
+ useEffect(() => {
123
+ if (!enabled) {
124
+ requestIdRef.current += 1;
125
+ setState(stopQuery);
126
+ return;
127
+ }
128
+ refetch().catch(() => void 0);
129
+ return () => {
130
+ requestIdRef.current += 1;
131
+ };
132
+ }, [
133
+ enabled,
134
+ queryKey,
135
+ refetch
136
+ ]);
137
+ const result = toQueryResult(state, refetch);
138
+ useDebugValue(result.status);
139
+ return result;
140
+ }
141
+ //#endregion
142
+ export { useQuery };