cushin-monorepo 3.0.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.
Files changed (103) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +14 -0
  3. package/.claude/settings.local.json +44 -0
  4. package/CHANGELOG.md +93 -0
  5. package/LICENSE +0 -0
  6. package/README.md +482 -0
  7. package/biome.json +34 -0
  8. package/dist/cli.d.ts +1 -0
  9. package/dist/cli.js +1552 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/config/index.d.ts +84 -0
  12. package/dist/config/index.js +69 -0
  13. package/dist/config/index.js.map +1 -0
  14. package/dist/config/schema.d.ts +43 -0
  15. package/dist/config/schema.js +14 -0
  16. package/dist/config/schema.js.map +1 -0
  17. package/dist/index.d.ts +27 -0
  18. package/dist/index.js +1666 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/runtime/client.d.ts +40 -0
  21. package/dist/runtime/client.js +260 -0
  22. package/dist/runtime/client.js.map +1 -0
  23. package/package.json +41 -0
  24. package/packages/api-codegen/CHANGELOG.md +86 -0
  25. package/packages/api-codegen/biome.json +34 -0
  26. package/packages/api-codegen/dist/cli.js +1038 -0
  27. package/packages/api-codegen/dist/cli.js.map +1 -0
  28. package/packages/api-codegen/dist/index.d.ts +103 -0
  29. package/packages/api-codegen/dist/index.js +1026 -0
  30. package/packages/api-codegen/dist/index.js.map +1 -0
  31. package/packages/api-codegen/node_modules/.bin/acorn +21 -0
  32. package/packages/api-codegen/node_modules/.bin/conventional-changelog +21 -0
  33. package/packages/api-codegen/node_modules/.bin/conventional-commits-parser +21 -0
  34. package/packages/api-codegen/node_modules/.bin/esbuild +21 -0
  35. package/packages/api-codegen/node_modules/.bin/eslint +21 -0
  36. package/packages/api-codegen/node_modules/.bin/jiti +21 -0
  37. package/packages/api-codegen/node_modules/.bin/next +21 -0
  38. package/packages/api-codegen/node_modules/.bin/tsc +21 -0
  39. package/packages/api-codegen/node_modules/.bin/tsserver +21 -0
  40. package/packages/api-codegen/node_modules/.bin/tsup +21 -0
  41. package/packages/api-codegen/node_modules/.bin/tsup-node +21 -0
  42. package/packages/api-codegen/node_modules/.bin/vitest +21 -0
  43. package/packages/api-codegen/package.json +88 -0
  44. package/packages/api-runtime/CHANGELOG.md +46 -0
  45. package/packages/api-runtime/README.md +95 -0
  46. package/packages/api-runtime/dist/chunk-3FFXWCVP.js +17 -0
  47. package/packages/api-runtime/dist/chunk-3FFXWCVP.js.map +1 -0
  48. package/packages/api-runtime/dist/chunk-EZ5P7OPH.js +267 -0
  49. package/packages/api-runtime/dist/chunk-EZ5P7OPH.js.map +1 -0
  50. package/packages/api-runtime/dist/client.d.ts +40 -0
  51. package/packages/api-runtime/dist/client.js +13 -0
  52. package/packages/api-runtime/dist/client.js.map +1 -0
  53. package/packages/api-runtime/dist/index.d.ts +3 -0
  54. package/packages/api-runtime/dist/index.js +21 -0
  55. package/packages/api-runtime/dist/index.js.map +1 -0
  56. package/packages/api-runtime/dist/schema.d.ts +45 -0
  57. package/packages/api-runtime/dist/schema.js +11 -0
  58. package/packages/api-runtime/dist/schema.js.map +1 -0
  59. package/packages/api-runtime/node_modules/.bin/esbuild +21 -0
  60. package/packages/api-runtime/node_modules/.bin/jiti +21 -0
  61. package/packages/api-runtime/node_modules/.bin/tsc +21 -0
  62. package/packages/api-runtime/node_modules/.bin/tsserver +21 -0
  63. package/packages/api-runtime/node_modules/.bin/tsup +21 -0
  64. package/packages/api-runtime/node_modules/.bin/tsup-node +21 -0
  65. package/packages/api-runtime/package.json +54 -0
  66. package/packages/cli/CHANGELOG.md +34 -0
  67. package/packages/cli/biome.json +34 -0
  68. package/packages/cli/dist/index.d.ts +27 -0
  69. package/packages/cli/dist/index.js +183 -0
  70. package/packages/cli/dist/index.js.map +1 -0
  71. package/packages/cli/node_modules/.bin/esbuild +21 -0
  72. package/packages/cli/node_modules/.bin/jiti +21 -0
  73. package/packages/cli/node_modules/.bin/tsc +21 -0
  74. package/packages/cli/node_modules/.bin/tsserver +21 -0
  75. package/packages/cli/node_modules/.bin/tsup +21 -0
  76. package/packages/cli/node_modules/.bin/tsup-node +21 -0
  77. package/packages/cli/package.json +47 -0
  78. package/pnpm-workspace.yaml +2 -0
  79. package/test-config.js +9 -0
  80. package/test-content-type-handling.mjs +100 -0
  81. package/test-endpoints-config.mjs +144 -0
  82. package/test-formdata-content-type-protection.mjs +127 -0
  83. package/test-formdata-runtime.mjs +127 -0
  84. package/test-full-integration.mjs +90 -0
  85. package/test-headers-formdata.mjs +97 -0
  86. package/test-headers-runtime.mjs +106 -0
  87. package/test-headers.mjs +79 -0
  88. package/test-internal-calls.mjs +57 -0
  89. package/test-ky-formdata.mjs +81 -0
  90. package/test-output/actions.ts +134 -0
  91. package/test-output/client.ts +81 -0
  92. package/test-output/hooks.ts +182 -0
  93. package/test-output/index.ts +9 -0
  94. package/test-output/prefetchs.ts +25 -0
  95. package/test-output/queries.ts +78 -0
  96. package/test-output/query-keys.ts +16 -0
  97. package/test-output/query-options.ts +38 -0
  98. package/test-output/server-client.ts +32 -0
  99. package/test-output/types.ts +61 -0
  100. package/test-real-endpoints.mjs +132 -0
  101. package/test-runtime-params.mjs +160 -0
  102. package/test-simple-config.mjs +71 -0
  103. package/tsconfig.base.json +29 -0
@@ -0,0 +1,106 @@
1
+ import {
2
+ defineEndpoint,
3
+ defineConfig,
4
+ createAPIClient,
5
+ } from "./packages/api-runtime/dist/index.js";
6
+ import { z } from "zod";
7
+
8
+ console.log("Testing runtime header filtering...\n");
9
+
10
+ // Create test endpoints
11
+ const config = defineConfig({
12
+ baseUrl: "https://httpbin.org",
13
+ endpoints: {
14
+ testWithHeaders: defineEndpoint({
15
+ path: "headers",
16
+ method: "GET",
17
+ response: z.any(),
18
+ headers: {
19
+ "X-Custom-Header": "custom-value",
20
+ "X-Keep-This": "keep",
21
+ },
22
+ }),
23
+ testWithMixedHeaders: defineEndpoint({
24
+ path: "headers",
25
+ method: "GET",
26
+ response: z.any(),
27
+ headers: {
28
+ "X-Custom-Header": "custom-value",
29
+ "X-Remove-Undefined": undefined,
30
+ "X-Remove-Null": null,
31
+ "X-Keep-This": "keep",
32
+ },
33
+ }),
34
+ testWithAllRemoved: defineEndpoint({
35
+ path: "headers",
36
+ method: "GET",
37
+ response: z.any(),
38
+ headers: {
39
+ "X-Remove-1": undefined,
40
+ "X-Remove-2": null,
41
+ },
42
+ }),
43
+ },
44
+ });
45
+
46
+ // Create API client
47
+ const client = createAPIClient(config);
48
+
49
+ console.log("Test 1: Headers with only string values");
50
+ try {
51
+ const response1 = await client.testWithHeaders();
52
+ console.log("✓ Request succeeded");
53
+ console.log(" Headers sent:", response1.headers);
54
+ if (
55
+ response1.headers["X-Custom-Header"] === "custom-value" &&
56
+ response1.headers["X-Keep-This"] === "keep"
57
+ ) {
58
+ console.log(" ✓ Both headers were sent correctly\n");
59
+ } else {
60
+ console.log(" ✗ Headers were not sent correctly\n");
61
+ }
62
+ } catch (error) {
63
+ console.log("✗ Request failed:", error.message, "\n");
64
+ }
65
+
66
+ console.log("Test 2: Headers with undefined and null values");
67
+ try {
68
+ const response2 = await client.testWithMixedHeaders();
69
+ console.log("✓ Request succeeded");
70
+ console.log(" Headers sent:", response2.headers);
71
+ if (
72
+ response2.headers["X-Custom-Header"] === "custom-value" &&
73
+ response2.headers["X-Keep-This"] === "keep" &&
74
+ !response2.headers["X-Remove-Undefined"] &&
75
+ !response2.headers["X-Remove-Null"]
76
+ ) {
77
+ console.log(
78
+ " ✓ Only non-null/undefined headers were sent (undefined/null headers filtered out)\n",
79
+ );
80
+ } else {
81
+ console.log(" ✗ Headers were not filtered correctly\n");
82
+ }
83
+ } catch (error) {
84
+ console.log("✗ Request failed:", error.message, "\n");
85
+ }
86
+
87
+ console.log("Test 3: All headers are undefined/null");
88
+ try {
89
+ const response3 = await client.testWithAllRemoved();
90
+ console.log("✓ Request succeeded");
91
+ console.log(" Headers sent:", response3.headers);
92
+ if (
93
+ !response3.headers["X-Remove-1"] &&
94
+ !response3.headers["X-Remove-2"]
95
+ ) {
96
+ console.log(
97
+ " ✓ All undefined/null headers were filtered out (no custom headers sent)\n",
98
+ );
99
+ } else {
100
+ console.log(" ✗ Headers were not filtered correctly\n");
101
+ }
102
+ } catch (error) {
103
+ console.log("✗ Request failed:", error.message, "\n");
104
+ }
105
+
106
+ console.log("Runtime tests completed! ✨");
@@ -0,0 +1,79 @@
1
+ import { defineEndpoint, defineConfig } from "./packages/api-runtime/dist/index.js";
2
+ import { z } from "zod";
3
+
4
+ console.log("Testing header unsetting functionality...\n");
5
+
6
+ // Test 1: Headers with string values
7
+ const endpoint1 = defineEndpoint({
8
+ path: "/test",
9
+ method: "GET",
10
+ response: z.object({ message: z.string() }),
11
+ headers: {
12
+ "X-Custom-Header": "value1",
13
+ "X-Another-Header": "value2",
14
+ },
15
+ });
16
+
17
+ console.log("✓ Test 1: Headers with string values");
18
+ console.log(" Input:", endpoint1.headers);
19
+ console.log(" Expected: All headers should be set");
20
+ console.log("");
21
+
22
+ // Test 2: Headers with undefined values (should be filtered out)
23
+ const endpoint2 = defineEndpoint({
24
+ path: "/test",
25
+ method: "GET",
26
+ response: z.object({ message: z.string() }),
27
+ headers: {
28
+ "X-Custom-Header": "value1",
29
+ "X-Remove-This": undefined,
30
+ "X-Another-Header": "value2",
31
+ },
32
+ });
33
+
34
+ console.log("✓ Test 2: Headers with undefined values");
35
+ console.log(" Input:", endpoint2.headers);
36
+ console.log(" Expected: X-Remove-This should be undefined");
37
+ console.log("");
38
+
39
+ // Test 3: Headers with null values (should be filtered out)
40
+ const endpoint3 = defineEndpoint({
41
+ path: "/test",
42
+ method: "GET",
43
+ response: z.object({ message: z.string() }),
44
+ headers: {
45
+ "X-Custom-Header": "value1",
46
+ "X-Remove-This": null,
47
+ "X-Another-Header": "value2",
48
+ },
49
+ });
50
+
51
+ console.log("✓ Test 3: Headers with null values");
52
+ console.log(" Input:", endpoint3.headers);
53
+ console.log(" Expected: X-Remove-This should be null");
54
+ console.log("");
55
+
56
+ // Test 4: Mixed headers
57
+ const endpoint4 = defineEndpoint({
58
+ path: "/test",
59
+ method: "POST",
60
+ response: z.object({ success: z.boolean() }),
61
+ body: z.object({ data: z.string() }),
62
+ headers: {
63
+ "X-Keep-This": "keep",
64
+ "X-Remove-Undefined": undefined,
65
+ "X-Remove-Null": null,
66
+ "X-Also-Keep": "also-keep",
67
+ },
68
+ });
69
+
70
+ console.log("✓ Test 4: Mixed headers");
71
+ console.log(" Input:", endpoint4.headers);
72
+ console.log(" Expected: Only X-Keep-This and X-Also-Keep should have string values");
73
+ console.log("");
74
+
75
+ console.log("All type checks passed! ✨");
76
+ console.log("\nThe implementation allows:");
77
+ console.log(" - Setting headers to string values");
78
+ console.log(" - Setting headers to undefined to unset them");
79
+ console.log(" - Setting headers to null to unset them");
@@ -0,0 +1,57 @@
1
+ import { createAPIClient } from "./packages/api-runtime/dist/client.js";
2
+ import simpleConfig from "./test-simple-config.mjs";
3
+
4
+ console.log("=== TESTING INTERNAL request() CALLS ===\n");
5
+ console.log("Analyzing what parameters are passed to this.request()\n");
6
+
7
+ const client = createAPIClient(simpleConfig);
8
+
9
+ // Inspect the actual generated function code
10
+ console.log("1. listUsers (no params, no query):");
11
+ console.log(client.listUsers.toString());
12
+ console.log(" → Calls: this.request(endpoint)");
13
+ console.log(" → params argument: NOT PASSED (undefined by default)\n");
14
+
15
+ console.log("2. searchUsers (query only):");
16
+ console.log(client.searchUsers.toString());
17
+ console.log(" → Calls: this.request(endpoint, undefined, query)");
18
+ console.log(" → params argument: EXPLICITLY undefined ⚠️\n");
19
+
20
+ console.log("3. getUser (params only):");
21
+ console.log(client.getUser.toString());
22
+ console.log(" → Calls: this.request(endpoint, params)");
23
+ console.log(" → params argument: passed\n");
24
+
25
+ console.log("4. getUserPosts (params + query):");
26
+ console.log(client.getUserPosts.toString());
27
+ console.log(" → Calls: this.request(endpoint, params, query)");
28
+ console.log(" → params argument: passed\n");
29
+
30
+ console.log("5. createSession (POST, no params, no body):");
31
+ console.log(client.createSession.toString());
32
+ console.log(" → Calls: this.request(endpoint)");
33
+ console.log(" → params argument: NOT PASSED (undefined by default)\n");
34
+
35
+ console.log("6. createUser (POST, body only):");
36
+ console.log(client.createUser.toString());
37
+ console.log(" → Calls: this.request(endpoint, undefined, undefined, body)");
38
+ console.log(" → params argument: EXPLICITLY undefined ⚠️\n");
39
+
40
+ console.log("7. updateUser (POST, params + body):");
41
+ console.log(client.updateUser.toString());
42
+ console.log(" → Calls: this.request(endpoint, params, undefined, body)");
43
+ console.log(" → params argument: passed\n");
44
+
45
+ console.log("\n=== ANALYSIS ===\n");
46
+ console.log("Situations where params is EXPLICITLY passed as undefined:");
47
+ console.log(" 1. GET với query only: this.request(endpoint, undefined, query)");
48
+ console.log(" 2. POST/etc với body only: this.request(endpoint, undefined, undefined, body)");
49
+ console.log("");
50
+ console.log("This is happening in generateMethods() at:");
51
+ console.log(" - Line 266-269 (GET with query only)");
52
+ console.log(" - Line 283-287 (POST with body only)");
53
+ console.log("");
54
+ console.log("✅ However, this.request() handles this correctly!");
55
+ console.log(" The buildPath() method checks 'if (!params) return path;'");
56
+ console.log(" So passing undefined explicitly is SAFE and has no issues.");
57
+ console.log("");
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Direct ky test to understand FormData behavior
3
+ */
4
+ import ky from 'ky';
5
+
6
+ console.log('\n🧪 Testing KY FormData Behavior\n');
7
+
8
+ // Create FormData
9
+ const formData = new FormData();
10
+ const fileBlob = new Blob(['test content'], { type: 'text/plain' });
11
+ formData.append('file', fileBlob, 'test.txt');
12
+ formData.append('metadata', JSON.stringify({ name: 'Test File' }));
13
+
14
+ console.log('FormData created with entries:', Array.from(formData.entries()).map(([k]) => k));
15
+
16
+ // Mock fetch to intercept
17
+ const originalFetch = globalThis.fetch;
18
+
19
+ globalThis.fetch = async (input, init) => {
20
+ console.log('\n📦 Fetch intercepted:');
21
+ console.log(' Input type:', input instanceof Request ? 'Request' : typeof input);
22
+
23
+ // If input is a Request object, extract info from it
24
+ if (input instanceof Request) {
25
+ console.log(' URL:', input.url);
26
+ console.log(' Method:', input.method);
27
+ console.log(' Body type:', input.body?.constructor?.name || 'none');
28
+ console.log(' Headers:');
29
+ for (const [key, value] of input.headers.entries()) {
30
+ console.log(` ${key}: ${value}`);
31
+ }
32
+ } else {
33
+ console.log(' URL:', typeof input === 'string' ? input : input.url);
34
+ console.log(' Method:', init?.method || 'GET');
35
+ console.log(' Body type:', init?.body?.constructor?.name || 'none');
36
+ console.log(' Body is FormData:', init?.body instanceof FormData);
37
+
38
+ const headers = init?.headers;
39
+ if (headers instanceof Headers) {
40
+ console.log(' Headers:');
41
+ for (const [key, value] of headers.entries()) {
42
+ console.log(` ${key}: ${value}`);
43
+ }
44
+ }
45
+ }
46
+
47
+ // Return proper Response object
48
+ return new Response(JSON.stringify({ success: true }), {
49
+ status: 200,
50
+ headers: { 'content-type': 'application/json' },
51
+ });
52
+ };
53
+
54
+ try {
55
+ // Test 1: Direct ky call with FormData using body option
56
+ console.log('\n✅ Test 1: ky with options.body (FormData)');
57
+ await ky('https://httpbin.org/post', {
58
+ method: 'POST',
59
+ body: formData,
60
+ });
61
+
62
+ console.log('\n✅ Test 2: ky.post with FormData');
63
+ const formData2 = new FormData();
64
+ formData2.append('test', 'value');
65
+
66
+ await ky.post('https://httpbin.org/post', {
67
+ body: formData2,
68
+ });
69
+
70
+ console.log('\n✅ Test 3: ky with json option');
71
+ await ky.post('https://httpbin.org/post', {
72
+ json: { test: 'value' },
73
+ });
74
+
75
+ } catch (error) {
76
+ console.error('❌ Error:', error.message);
77
+ } finally {
78
+ globalThis.fetch = originalFetch;
79
+ }
80
+
81
+ console.log('\n🎉 Ky test complete!\n');
@@ -0,0 +1,134 @@
1
+ "use server";
2
+
3
+ import { revalidateTag, revalidatePath } from "next/cache";
4
+ import { serverClient } from "./server-client";
5
+ import type {
6
+ APIEndpoints,
7
+ ExtractBody,
8
+ ExtractParams,
9
+ ExtractResponse,
10
+ } from "./types";
11
+
12
+ export type ActionResult<T> =
13
+ | { success: true; data: T }
14
+ | { success: false; error: string };
15
+
16
+ /**
17
+ * Create new session
18
+ * @tags auth
19
+ */
20
+ export async function createSessionAction(): Promise<
21
+ ActionResult<ExtractResponse<APIEndpoints["createSession"]>>
22
+ > {
23
+ try {
24
+ const result = await serverClient.createSession();
25
+
26
+ // Revalidate related data
27
+ revalidateTag("auth");
28
+
29
+ return { success: true, data: result };
30
+ } catch (error) {
31
+ console.error("[Server Action Error]:", error);
32
+ return {
33
+ success: false,
34
+ error: error instanceof Error ? error.message : "Unknown error",
35
+ };
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Create new user
41
+ * @tags users
42
+ */
43
+ export async function createUserAction(
44
+ body: ExtractBody<APIEndpoints["createUser"]>,
45
+ ): Promise<ActionResult<ExtractResponse<APIEndpoints["createUser"]>>> {
46
+ try {
47
+ const result = await serverClient.createUser(body);
48
+
49
+ // Revalidate related data
50
+ revalidateTag("users");
51
+
52
+ return { success: true, data: result };
53
+ } catch (error) {
54
+ console.error("[Server Action Error]:", error);
55
+ return {
56
+ success: false,
57
+ error: error instanceof Error ? error.message : "Unknown error",
58
+ };
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Update user
64
+ * @tags users
65
+ */
66
+ export async function updateUserAction(input: {
67
+ params: ExtractParams<APIEndpoints["updateUser"]>;
68
+ body: ExtractBody<APIEndpoints["updateUser"]>;
69
+ }): Promise<ActionResult<ExtractResponse<APIEndpoints["updateUser"]>>> {
70
+ try {
71
+ const result = await (serverClient as any).updateUser(
72
+ input.params,
73
+ input.body,
74
+ );
75
+
76
+ // Revalidate related data
77
+ revalidateTag("users");
78
+
79
+ return { success: true, data: result };
80
+ } catch (error) {
81
+ console.error("[Server Action Error]:", error);
82
+ return {
83
+ success: false,
84
+ error: error instanceof Error ? error.message : "Unknown error",
85
+ };
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Delete user
91
+ * @tags users
92
+ */
93
+ export async function deleteUserAction(
94
+ params: ExtractParams<APIEndpoints["deleteUser"]>,
95
+ ): Promise<ActionResult<ExtractResponse<APIEndpoints["deleteUser"]>>> {
96
+ try {
97
+ const result = await serverClient.deleteUser(params);
98
+
99
+ // Revalidate related data
100
+ revalidateTag("users");
101
+
102
+ return { success: true, data: result };
103
+ } catch (error) {
104
+ console.error("[Server Action Error]:", error);
105
+ return {
106
+ success: false,
107
+ error: error instanceof Error ? error.message : "Unknown error",
108
+ };
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Refresh access token
114
+ * @tags auth
115
+ */
116
+ export async function refreshTokenAction(): Promise<
117
+ ActionResult<ExtractResponse<APIEndpoints["refreshToken"]>>
118
+ > {
119
+ try {
120
+ const result = await serverClient.refreshToken();
121
+
122
+ // Revalidate related data
123
+ revalidateTag("auth");
124
+
125
+ return { success: true, data: result };
126
+ } catch (error) {
127
+ console.error("[Server Action Error]:", error);
128
+ return {
129
+ success: false,
130
+ error: error instanceof Error ? error.message : "Unknown error",
131
+ };
132
+ }
133
+ }
134
+
@@ -0,0 +1,81 @@
1
+ 'use client';
2
+
3
+ import { createAPIClient } from '@cushin/api-runtime';
4
+ import type { AuthCallbacks } from '@cushin/api-runtime';
5
+ import { apiConfig } from '../test-real-endpoints.mjs';
6
+ import { z } from 'zod';
7
+
8
+ // Type the methods based on endpoints
9
+ type APIClientMethods = {
10
+ [K in keyof typeof apiConfig.endpoints]: (typeof apiConfig.endpoints)[K] extends infer E
11
+ ? E extends { method: "GET"; params: infer P; query: infer Q; response: infer R }
12
+ ? (params: P extends z.ZodType ? z.infer<P> : never, query?: Q extends z.ZodType ? z.infer<Q> : never) => Promise<R extends z.ZodType ? z.infer<R> : never>
13
+ : E extends { method: "GET"; params: infer P; response: infer R }
14
+ ? (params: P extends z.ZodType ? z.infer<P> : never) => Promise<R extends z.ZodType ? z.infer<R> : never>
15
+ : E extends { method: "GET"; query: infer Q; response: infer R }
16
+ ? (query?: Q extends z.ZodType ? z.infer<Q> : never) => Promise<R extends z.ZodType ? z.infer<R> : never>
17
+ : E extends { method: "GET"; response: infer R }
18
+ ? () => Promise<R extends z.ZodType ? z.infer<R> : never>
19
+ : E extends { method: string; params: infer P; body: infer B; response: infer R }
20
+ ? (params: P extends z.ZodType ? z.infer<P> : never, body: B extends z.ZodType ? z.infer<B> : never) => Promise<R extends z.ZodType ? z.infer<R> : never>
21
+ : E extends { method: string; params: infer P; response: infer R }
22
+ ? (params: P extends z.ZodType ? z.infer<P> : never) => Promise<R extends z.ZodType ? z.infer<R> : never>
23
+ : E extends { method: string; body: infer B; response: infer R }
24
+ ? (body: B extends z.ZodType ? z.infer<B> : never) => Promise<R extends z.ZodType ? z.infer<R> : never>
25
+ : E extends { method: string; response: infer R }
26
+ ? () => Promise<R extends z.ZodType ? z.infer<R> : never>
27
+ : never
28
+ : never;
29
+ };
30
+
31
+
32
+ // Export singleton instance (will be initialized later)
33
+ export let baseClient: APIClientMethods & {
34
+ refreshAuth: () => Promise<void>;
35
+ updateAuthCallbacks: (callbacks: AuthCallbacks) => void;
36
+ };
37
+
38
+ export const apiClient = {
39
+ listUsers: (): Promise<z.infer<NonNullable<typeof apiConfig.endpoints.listUsers.response>>> =>
40
+ (baseClient as any).listUsers(),
41
+ getUser: (params: z.infer<NonNullable<typeof apiConfig.endpoints.getUser.params>>): Promise<z.infer<NonNullable<typeof apiConfig.endpoints.getUser.response>>> =>
42
+ (baseClient as any).getUser(params),
43
+ searchUsers: (query?: z.infer<NonNullable<typeof apiConfig.endpoints.searchUsers.query>>): Promise<z.infer<NonNullable<typeof apiConfig.endpoints.searchUsers.response>>> =>
44
+ (baseClient as any).searchUsers(query),
45
+ getUserPosts: (params: z.infer<NonNullable<typeof apiConfig.endpoints.getUserPosts.params>>, query?: z.infer<NonNullable<typeof apiConfig.endpoints.getUserPosts.query>>): Promise<z.infer<NonNullable<typeof apiConfig.endpoints.getUserPosts.response>>> =>
46
+ (baseClient as any).getUserPosts(params, query),
47
+ createSession: (): Promise<z.infer<NonNullable<typeof apiConfig.endpoints.createSession.response>>> =>
48
+ (baseClient as any).createSession(),
49
+ createUser: (body: z.infer<NonNullable<typeof apiConfig.endpoints.createUser.body>>): Promise<z.infer<NonNullable<typeof apiConfig.endpoints.createUser.response>>> =>
50
+ (baseClient as any).createUser(body),
51
+ updateUser: (params: z.infer<NonNullable<typeof apiConfig.endpoints.updateUser.params>>, body: z.infer<NonNullable<typeof apiConfig.endpoints.updateUser.body>>): Promise<z.infer<NonNullable<typeof apiConfig.endpoints.updateUser.response>>> =>
52
+ (baseClient as any).updateUser(params, body),
53
+ deleteUser: (params: z.infer<NonNullable<typeof apiConfig.endpoints.deleteUser.params>>): Promise<z.infer<NonNullable<typeof apiConfig.endpoints.deleteUser.response>>> =>
54
+ (baseClient as any).deleteUser(params),
55
+ refreshToken: (): Promise<z.infer<NonNullable<typeof apiConfig.endpoints.refreshToken.response>>> =>
56
+ (baseClient as any).refreshToken(),
57
+ };
58
+
59
+ /**
60
+ * Initialize API client with auth callbacks
61
+ * Call this function in your auth provider setup
62
+ *
63
+ * @example
64
+ * const authCallbacks = {
65
+ * getTokens: () => getStoredTokens(),
66
+ * onAuthError: () => router.push('/login'),
67
+ * onRefreshToken: async () => {
68
+ * await refreshAccessToken();
69
+ * },
70
+ * };
71
+ *
72
+ * initializeAPIClient(authCallbacks);
73
+ */
74
+ export const initializeAPIClient = (authCallbacks: AuthCallbacks) => {
75
+ baseClient = createAPIClient(apiConfig, authCallbacks) as any;
76
+ return baseClient;
77
+ };
78
+
79
+ // Export for custom usage
80
+ export { createAPIClient };
81
+ export type { AuthCallbacks };