Skip to content

Commit c8c9da7

Browse files
authored
Merge pull request #13 from dev-five-git/query-issue
Fix query issue
2 parents d0a2e9b + 9062c65 commit c8c9da7

File tree

7 files changed

+90
-8
lines changed

7 files changed

+90
-8
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"changes":{"packages/fetch/package.json":"Patch","packages/core/package.json":"Patch"},"note":"Fix query issue","date":"2025-12-04T03:48:47.984060900Z"}

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,6 @@ jobs:
6969
token: ${{ secrets.CODECOV_TOKEN }}
7070
fail_ci_if_error: true
7171
files: ./coverage/lcov.info
72-
- run: bun run publish --access public --ignore-scripts
72+
- run: bunx @changepacks/cli publish
7373
env:
7474
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}

packages/core/src/additional.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ export type RequiredOptions<T extends object> = keyof T extends undefined
1111
export type DevupApiRequestInit = Omit<RequestInit, 'body'> & {
1212
body?: object | RequestInit['body']
1313
params?: Record<string, string | number | boolean | null | undefined>
14-
query?: Record<string, string | number | boolean | null | undefined>
14+
query?:
15+
| ConstructorParameters<typeof URLSearchParams>[0]
16+
| Record<string, string | number | (number | string)[]>
1517
middleware?: Middleware[]
1618
}
1719

packages/core/src/middleware.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import type { PromiseOr } from './utils'
44
export interface MiddlewareCallbackParams {
55
request: Request
66
schemaPath: string
7-
params?: Record<string, unknown>
8-
query?: Record<string, unknown>
7+
params?: DevupApiRequestInit['params']
8+
query?: DevupApiRequestInit['query']
99
headers?: DevupApiRequestInit['headers']
1010
body?: DevupApiRequestInit['body']
1111
}

packages/fetch/src/__tests__/utils.test.ts

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect, test } from 'bun:test'
2-
import { getApiEndpoint, isPlainObject } from '../utils'
2+
import { getApiEndpoint, getQueryString, isPlainObject } from '../utils'
33

44
test.each([
55
[{}, true],
@@ -106,3 +106,57 @@ test.each([
106106
])('getApiEndpoint: baseUrl=%s, path=%s, params=%s -> %s', (baseUrl, path, params, expected) => {
107107
expect(getApiEndpoint(baseUrl, path, params)).toBe(expected)
108108
})
109+
110+
test.each([
111+
['a=1&b=2', 'a=1&b=2'],
112+
['', ''],
113+
['key=value&test=123', 'key=value&test=123'],
114+
['x=1&y=2&z=3', 'x=1&y=2&z=3'],
115+
[{ a: '1', b: '2' }, 'a=1&b=2'],
116+
[{}, ''],
117+
[{ key: 'value', test: '123' }, 'key=value&test=123'],
118+
[{ x: '1', y: '2', z: '3' }, 'x=1&y=2&z=3'],
119+
[{ a: 1, b: 2 }, 'a=1&b=2'],
120+
[{ a: '1', b: 2, c: 'test' }, 'a=1&b=2&c=test'],
121+
[{ a: ['1', '2', '3'] }, 'a=1&a=2&a=3'],
122+
[{ a: [1, 2, 3] }, 'a=1&a=2&a=3'],
123+
[{ a: [1, '2', 3] }, 'a=1&a=2&a=3'],
124+
[new URLSearchParams('a=1&b=2'), 'a=1&b=2'],
125+
[new URLSearchParams(''), ''],
126+
[new URLSearchParams('key=value&test=123'), 'key=value&test=123'],
127+
[
128+
[
129+
['a', '1'],
130+
['b', '2'],
131+
] as [string, string][],
132+
'a=1&b=2',
133+
],
134+
[
135+
[
136+
['key', 'value'],
137+
['test', '123'],
138+
] as [string, string][],
139+
'key=value&test=123',
140+
],
141+
[
142+
[
143+
['x', '1'],
144+
['y', '2'],
145+
['z', '3'],
146+
] as [string, string][],
147+
'x=1&y=2&z=3',
148+
],
149+
[
150+
[
151+
['x', '1'],
152+
['x', '2'],
153+
['x', '3'],
154+
] as [string, string][],
155+
'x=1&x=2&x=3',
156+
],
157+
])('getQueryString: %s query -> "%s"', (query, expected) => {
158+
const result = getQueryString(
159+
query as NonNullable<Parameters<typeof getQueryString>[0]>,
160+
)
161+
expect(result.toString()).toBe(expected)
162+
})

packages/fetch/src/api.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import type {
2222
} from '@devup-api/core'
2323
import { convertResponse } from './response-converter'
2424
import { getApiEndpointInfo } from './url-map'
25-
import { getApiEndpoint, isPlainObject } from './utils'
25+
import { getApiEndpoint, getQueryString, isPlainObject } from './utils'
2626

2727
// biome-ignore lint/suspicious/noExplicitAny: any is used to allow for flexibility in the type
2828
export type DevupApiResponse<T, E = any> =
@@ -236,7 +236,7 @@ export class DevupApi<S extends ConditionalKeys<DevupApiServers>> {
236236
DevupApiResponse<ExtractValue<O, 'response'>, ExtractValue<O, 'error'>>
237237
> {
238238
const { method, url } = getApiEndpointInfo(path, this.serverName)
239-
const { middleware = [], ...restOptions } = options[0] || {}
239+
const { middleware = [], query, ...restOptions } = options[0] || {}
240240
const mergedOptions = {
241241
...this.defaultOptions,
242242
...restOptions,
@@ -248,6 +248,7 @@ export class DevupApi<S extends ConditionalKeys<DevupApiServers>> {
248248
if (requestOptions.body && isPlainObject(requestOptions.body)) {
249249
requestOptions.body = JSON.stringify(requestOptions.body)
250250
}
251+
const queryString = query ? `?${getQueryString(query).toString()}` : ''
251252
let request = new Request(
252253
getApiEndpoint(
253254
this.baseUrl,
@@ -260,7 +261,7 @@ export class DevupApi<S extends ConditionalKeys<DevupApiServers>> {
260261
>
261262
}
262263
).params,
263-
),
264+
) + queryString,
264265
requestOptions as RequestInit,
265266
)
266267

packages/fetch/src/utils.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { DevupApiRequestInit } from '@devup-api/core'
2+
13
export function isPlainObject(obj: unknown): obj is object {
24
if (obj === null || typeof obj !== 'object') return false
35

@@ -16,3 +18,25 @@ export function getApiEndpoint(
1618
}
1719
return ret
1820
}
21+
22+
export function getQueryString(
23+
query: NonNullable<DevupApiRequestInit['query']>,
24+
): URLSearchParams {
25+
if (typeof query === 'string') {
26+
return new URLSearchParams(query)
27+
}
28+
if (isPlainObject(query)) {
29+
const params = new URLSearchParams()
30+
for (const [key, value] of Object.entries(query)) {
31+
if (Array.isArray(value)) {
32+
for (const v of value) {
33+
params.append(key, String(v))
34+
}
35+
} else {
36+
params.append(key, String(value))
37+
}
38+
}
39+
return params
40+
}
41+
return new URLSearchParams(query)
42+
}

0 commit comments

Comments
 (0)