Skip to content

Commit 6b82416

Browse files
committed
Merge branch 'ofetch' of github.com:hungify/vue-typescript-stater into ofetch
2 parents 08a40e5 + 6647391 commit 6b82416

File tree

4 files changed

+154
-40
lines changed

4 files changed

+154
-40
lines changed

bun.lockb

390 KB
Binary file not shown.

package.json

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,53 +16,54 @@
1616
"test:e2e": "start-server-and-test preview http://localhost:4173 'cypress run --e2e'",
1717
"test:e2e:dev": "start-server-and-test 'vite dev --port 4173' http://localhost:4173 'cypress open --e2e'",
1818
"postinstall": "run-p build:icons",
19-
"generate:api": "openapi-typescript http://localhost:8000/api/docs-yaml -o src/generated/api-schema.d.ts"
19+
"generate:api": "openapi-typescript src/openapi/api-schema.yaml -o src/generated/api-schema.d.ts"
2020
},
2121
"dependencies": {
22-
"@unocss/reset": "^0.58.4",
23-
"@vueuse/core": "^10.7.2",
24-
"@vueuse/integrations": "^10.8.0",
25-
"openapi-fetch": "^0.9.2",
26-
"openapi-typescript-helpers": "^0.0.7",
27-
"pinia": "^2.1.7",
28-
"ufo": "^1.5.3",
29-
"universal-cookie": "^7.1.0",
22+
"@unocss/reset": "^0.58.9",
23+
"@vueuse/core": "^10.11.0",
24+
"@vueuse/integrations": "^10.11.0",
25+
"ofetch": "^1.3.4",
26+
"openapi-typescript": "^7.3.0",
27+
"openapi-typescript-helpers": "^0.0.11",
28+
"pinia": "^2.2.0",
29+
"ufo": "^1.5.4",
30+
"universal-cookie": "^7.2.0",
3031
"valibot": "^0.26.0",
31-
"vue": "^3.4.15",
32-
"vue-router": "^4.2.5"
32+
"vue": "^3.4.35",
33+
"vue-router": "^4.4.2"
3334
},
3435
"devDependencies": {
35-
"@iconify/tools": "^4.0.0",
36+
"@iconify/tools": "^4.0.4",
3637
"@iconify/types": "^2.0.0",
37-
"@iconify/utils": "^2.1.16",
38-
"@iconify/vue": "^4.1.1",
39-
"@sxzz/eslint-config": "^3.7.6",
40-
"@sxzz/prettier-config": "^2.0.0",
41-
"@types/node": "^20.11.5",
42-
"@vitejs/plugin-vue": "^5.0.3",
43-
"@vue/compiler-sfc": "^3.4.15",
44-
"@vue/test-utils": "^2.4.3",
45-
"cypress": "^13.6.3",
46-
"eslint": "^8.56.0",
47-
"jsdom": "^24.0.0",
38+
"@iconify/utils": "^2.1.29",
39+
"@iconify/vue": "^4.1.2",
40+
"@sxzz/eslint-config": "^3.16.1",
41+
"@sxzz/prettier-config": "^2.0.2",
42+
"@types/node": "^20.14.14",
43+
"@vitejs/plugin-vue": "^5.1.2",
44+
"@vue/compiler-sfc": "^3.4.35",
45+
"@vue/test-utils": "^2.4.6",
46+
"cypress": "^13.13.2",
47+
"eslint": "^8.57.0",
48+
"jsdom": "^24.1.1",
4849
"npm-run-all": "^4.1.5",
49-
"openapi-typescript": "^6.7.4",
50-
"picocolors": "^1.0.0",
51-
"prettier": "^3.2.4",
52-
"start-server-and-test": "^2.0.3",
50+
"picocolors": "^1.0.1",
51+
"prettier": "^3.3.3",
52+
"start-server-and-test": "^2.0.5",
5353
"ts-reset": "^0.0.1",
54-
"tsx": "^4.7.1",
55-
"typescript": "^5.3.3",
56-
"unocss": "^0.58.4",
57-
"unplugin-auto-import": "^0.17.3",
54+
"tsx": "^4.16.5",
55+
"type-fest": "^4.23.0",
56+
"typescript": "^5.5.4",
57+
"unocss": "^0.58.9",
58+
"unplugin-auto-import": "^0.17.8",
5859
"unplugin-vue-components": "^0.26.0",
5960
"unplugin-vue-router": "^0.7.0",
60-
"vite": "^5.0.12",
61-
"vite-plugin-pages": "^0.32.0",
62-
"vite-plugin-vue-devtools": "^7.0.11",
61+
"vite": "^5.3.5",
62+
"vite-plugin-pages": "^0.32.3",
63+
"vite-plugin-vue-devtools": "^7.3.7",
6364
"vite-plugin-vue-layouts": "^0.11.0",
64-
"vite-plugin-webfont-dl": "^3.9.1",
65-
"vitest": "^1.2.1",
65+
"vite-plugin-webfont-dl": "^3.9.4",
66+
"vitest": "^1.6.0",
6667
"vue-tsc": "^1.8.27"
6768
},
6869
"simple-git-hooks": {

src/utils/fetch.ts

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import {
2+
$fetch,
3+
type FetchContext,
4+
type FetchError,
5+
type FetchOptions,
6+
} from 'ofetch'
7+
import type {
8+
ErrorResponse,
9+
MediaType,
10+
OperationRequestBodyContent,
11+
ResponseObjectMap,
12+
SuccessResponse,
13+
} from 'openapi-typescript-helpers'
14+
15+
export type FetchResponseData<
16+
T,
17+
Media extends MediaType = MediaType,
18+
> = SuccessResponse<ResponseObjectMap<T>, Media>
19+
20+
export type FetchResponseError<
21+
T,
22+
Media extends MediaType = MediaType,
23+
> = FetchError<ErrorResponse<ResponseObjectMap<T>, Media>>
24+
25+
export type MethodOption<M, P> = 'get' extends keyof P
26+
? { method?: M }
27+
: { method: M }
28+
29+
export type ParamsOption<T> = T extends { parameters?: any; query?: any }
30+
? T['parameters']
31+
: Record<string, never>
32+
33+
export type RequestBodyOption<T> =
34+
OperationRequestBodyContent<T> extends never
35+
? { body?: never }
36+
: undefined extends OperationRequestBodyContent<T>
37+
? { body?: OperationRequestBodyContent<T> }
38+
: { body: OperationRequestBodyContent<T> }
39+
40+
export type FilterMethods<T> = {
41+
[K in keyof Omit<T, 'parameters'> as T[K] extends never | undefined
42+
? never
43+
: K]: T[K]
44+
}
45+
46+
export type OpenFetchOptions<
47+
Method,
48+
LowercasedMethod,
49+
Params,
50+
Operation = 'get' extends LowercasedMethod
51+
? 'get' extends keyof Params
52+
? Params['get']
53+
: never
54+
: LowercasedMethod extends keyof Params
55+
? Params[LowercasedMethod]
56+
: never,
57+
> = MethodOption<Method, Params> &
58+
ParamsOption<Operation> &
59+
RequestBodyOption<Operation> &
60+
Omit<FetchOptions, 'query' | 'body' | 'method'>
61+
62+
export type OpenFetchClient<Paths> = <
63+
ReqT extends Extract<keyof Paths, string>,
64+
Methods extends FilterMethods<Paths[ReqT]>,
65+
Method extends
66+
| Extract<keyof Methods, string>
67+
| Uppercase<Extract<keyof Methods, string>>,
68+
LowercasedMethod extends Lowercase<Method> extends keyof FilterMethods<
69+
Paths[ReqT]
70+
>
71+
? Lowercase<Method>
72+
: never,
73+
DefaultMethod extends 'get' extends LowercasedMethod
74+
? 'get'
75+
: LowercasedMethod,
76+
ResT = FetchResponseData<Paths[ReqT][DefaultMethod]>,
77+
ErrorT = FetchResponseError<Methods[DefaultMethod]>,
78+
>(
79+
url: ReqT,
80+
options?: OpenFetchOptions<Method, LowercasedMethod, Methods>,
81+
) => Promise<ResT | ErrorT>
82+
83+
// More flexible way to rewrite the request path,
84+
// but has problems - https://github.com/unjs/ofetch/issues/319
85+
export function openFetchRequestInterceptor(ctx: FetchContext) {
86+
ctx.request = fillPath(
87+
ctx.request as string,
88+
(ctx.options as { path: Record<string, string> }).path,
89+
)
90+
}
91+
92+
export function createOpenFetch<Paths>(
93+
options: FetchOptions | ((options: FetchOptions) => FetchOptions),
94+
): OpenFetchClient<Paths> {
95+
return (url: string, opts: any = {}) => {
96+
return $fetch(
97+
fillPath(url, opts?.path),
98+
typeof options === 'function'
99+
? options(opts)
100+
: {
101+
...options,
102+
...opts,
103+
},
104+
)
105+
}
106+
}
107+
108+
function fillPath(path: string, params: object = {}) {
109+
for (const [k, v] of Object.entries(params))
110+
path = path.replace(`{${k}}`, encodeURIComponent(String(v)))
111+
return path
112+
}

tsconfig.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
"baseUrl": ".",
1010
/* Bundler mode */
1111
"module": "ESNext",
12-
"moduleResolution": "bundler",
12+
"moduleResolution": "Bundler",
1313
"paths": {
14-
"#/*": ["src/*"],
14+
"#/*": ["src/*"]
1515
},
1616
"resolveJsonModule": true,
1717
"allowImportingTsExtensions": true,
@@ -24,11 +24,12 @@
2424
"isolatedModules": true,
2525
"verbatimModuleSyntax": true,
2626
"skipLibCheck": true,
27+
"noUncheckedIndexedAccess": true
2728
},
2829
"include": [
2930
"src/**/*.ts",
3031
"src/**/*.d.ts",
3132
"src/**/*.vue",
32-
"typed-router.d.ts",
33-
],
33+
"typed-router.d.ts"
34+
]
3435
}

0 commit comments

Comments
 (0)