fix: app user agent for api-client reqs using tauri http plugin (#6045)

fix: app user agent
This commit is contained in:
Calum H.
2026-05-08 20:52:52 +01:00
committed by GitHub
parent 7048a35e9f
commit a082e8597c
9 changed files with 80 additions and 66 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ import { XHRUploadClient } from './xhr-upload-client'
* const client = new GenericModrinthClient({
* userAgent: 'my-app/1.0.0',
* features: [
* new AuthFeature({ token: 'mrp_...' }),
* new AuthFeature({ token: async () => getOAuthToken() }),
* new RetryFeature({ maxAttempts: 3 })
* ]
* })
+8 -4
View File
@@ -66,13 +66,17 @@ export interface NuxtClientConfig extends ClientConfig {
* ```typescript
* // In a Nuxt composable
* const config = useRuntimeConfig()
* const auth = await useAuth()
*
* const client = new NuxtModrinthClient({
* userAgent: 'my-nuxt-app/1.0.0',
* rateLimitKey: import.meta.server ? config.rateLimitKey : undefined,
* features: [
* new AuthFeature({ token: () => auth.value.token })
* new AuthFeature({
* token: async () => getOAuthToken()
* }),
* new CircuitBreakerFeature({
* storage: new NuxtCircuitBreakerStorage()
* })
* ]
* })
*
@@ -171,9 +175,9 @@ export class NuxtModrinthClient extends XHRUploadClient {
return super.normalizeError(error)
}
protected buildDefaultHeaders(): Record<string, string> {
protected async buildDefaultHeaders(): Promise<Record<string, string>> {
const headers: Record<string, string> = {
...super.buildDefaultHeaders(),
...(await super.buildDefaultHeaders()),
}
// Use the resolved key (populated by resolveRateLimitKey in request())
+2 -3
View File
@@ -27,11 +27,10 @@ interface HttpError extends Error {
* ```typescript
* import { getVersion } from '@tauri-apps/api/app'
*
* const version = await getVersion()
* const client = new TauriModrinthClient({
* userAgent: `modrinth/theseus/${version} (support@modrinth.com)`,
* userAgent: async () => `modrinth/theseus/${await getVersion()} (support@modrinth.com)`,
* features: [
* new AuthFeature({ token: 'mrp_...' })
* new AuthFeature({ token: async () => getOAuthToken() })
* ]
* })
*
@@ -27,50 +27,57 @@ export abstract class XHRUploadClient extends AbstractModrinthClient {
const url = this.buildUrl(path, baseUrl, options.version)
// For FormData uploads, don't set Content-Type (let browser set multipart boundary)
// For file uploads, use application/octet-stream
const isFormData = 'formData' in options && options.formData instanceof FormData
const baseHeaders = this.buildDefaultHeaders()
// Remove Content-Type for FormData so browser can set multipart/form-data with boundary
if (isFormData) {
delete baseHeaders['Content-Type']
} else {
baseHeaders['Content-Type'] = 'application/octet-stream'
}
const mergedOptions: UploadRequestOptions = {
retry: false, // default: don't retry uploads
...options,
headers: {
...baseHeaders,
...options.headers,
},
}
this.attachArchonSentryCaptureHeader(mergedOptions)
const context = this.buildUploadContext(url, path, mergedOptions)
const progressCallbacks: Array<(p: UploadProgress) => void> = []
if (mergedOptions.onProgress) {
progressCallbacks.push(mergedOptions.onProgress)
if (options.onProgress) {
progressCallbacks.push(options.onProgress)
}
const abortController = new AbortController()
if (mergedOptions.signal) {
mergedOptions.signal.addEventListener('abort', () => abortController.abort())
if (options.signal) {
options.signal.addEventListener('abort', () => abortController.abort())
}
let context: RequestContext | undefined
const handle: UploadHandle<T> = {
promise: this.executeUploadFeatureChain<T>(context, progressCallbacks, abortController)
.then(async (result) => {
await this.config.hooks?.onResponse?.(result, context)
return result
})
.catch(async (error) => {
const apiError = this.normalizeError(error, context)
promise: (async () => {
const isFormData = 'formData' in options && options.formData instanceof FormData
const baseHeaders = await this.buildDefaultHeaders()
if (isFormData) {
delete baseHeaders['Content-Type']
} else {
baseHeaders['Content-Type'] = 'application/octet-stream'
}
const mergedOptions: UploadRequestOptions = {
retry: false,
...options,
headers: {
...baseHeaders,
...options.headers,
},
}
this.attachArchonSentryCaptureHeader(mergedOptions)
const uploadContext = this.buildUploadContext(url, path, mergedOptions)
context = uploadContext
if (abortController.signal.aborted) {
throw new ModrinthApiError('Upload cancelled')
}
const result = await this.executeUploadFeatureChain<T>(
uploadContext,
progressCallbacks,
abortController,
)
await this.config.hooks?.onResponse?.(result, uploadContext)
return result
})().catch(async (error) => {
const apiError = this.normalizeError(error, context)
if (context) {
await this.config.hooks?.onError?.(apiError, context)
throw apiError
}),
}
throw apiError
}),
onProgress: (callback) => {
progressCallbacks.push(callback)
return handle