Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,18 +122,23 @@ func (ctx *EventContext) MustUnmarshalForm(v interface{}) {

func (ctx *EventContext) UnmarshalForm(v interface{}) (err error) {
mf := ctx.R.MultipartForm
if ctx.R.MultipartForm == nil {
var values map[string][]string
if mf != nil {
values = mf.Value
} else if ctx.R.Form != nil {
values = ctx.R.Form
} else {
return
}

dec := form.NewDecoder()
err = dec.Decode(v, mf.Value)
err = dec.Decode(v, values)
if err != nil {
// panic(err)
return
}

if len(mf.File) > 0 {
if mf != nil && len(mf.File) > 0 {
for k, vs := range mf.File {
_ = reflectutils.Set(v, k, vs)
}
Expand Down
4 changes: 2 additions & 2 deletions builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ func (b *Builder) PacksHandler(contentType string, packs ...ComponentsPack) http
buf.WriteString("\n\n")
}

body := bytes.NewReader(buf.Bytes())
bodyBytes := buf.Bytes()

return gziphandler.GzipHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", contentType)
http.ServeContent(w, r, "", startTime, body)
http.ServeContent(w, r, "", startTime, bytes.NewReader(bodyBytes))
}))
}

Expand Down
71 changes: 46 additions & 25 deletions corejs/src/fetchInterceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,39 @@ const requestMap = new Map<string, { resource: RequestInfo | URL; config?: Reque

const originalFetch: typeof window.fetch = window.fetch

function isCrossOrigin(resource: RequestInfo | URL): boolean {
let urlStr: string
if (typeof resource === 'string') {
urlStr = resource
} else if (resource instanceof URL) {
urlStr = resource.toString()
} else if (typeof Request !== 'undefined' && resource instanceof Request) {
urlStr = resource.url
} else {
return false
}
try {
const url = new URL(urlStr, window.location.href)
return url.origin !== window.location.origin
} catch {
return false
}
}

function notifyResponse(
requestId: string,
response: Response,
customInterceptor: FetchInterceptor
) {
const requestInfo = requestMap.get(requestId)
if (customInterceptor.onResponse && requestInfo) {
const resource =
requestInfo.resource instanceof URL ? requestInfo.resource.toString() : requestInfo.resource
customInterceptor.onResponse(requestId, response, resource, requestInfo.config)
}
requestMap.delete(requestId)
}

export function initFetchInterceptor(customInterceptor: FetchInterceptor) {
// do not rewrite fetch in test env
if (typeof window.__vitest_environment__ !== 'undefined') return
Expand All @@ -22,6 +55,12 @@ export function initFetchInterceptor(customInterceptor: FetchInterceptor) {
): Promise<Response> {
const [resource, config] = args

// Skip cross-origin requests (e.g. Google Analytics): we have no insight
// into them and forcing a JSON parse on their bodies just produces noise.
if (isCrossOrigin(resource)) {
return originalFetch(...args)
}

// Generate a unique ID for the request
const requestId = generateUniqueId()

Expand All @@ -39,31 +78,13 @@ export function initFetchInterceptor(customInterceptor: FetchInterceptor) {
// Clone the response to preserve the original response for further use
const clonedResponse = response.clone()

// Start processing the response body without waiting
const processingPromise = clonedResponse.json()

processingPromise
.then(() => {
const requestInfo = requestMap.get(requestId)

if (customInterceptor.onResponse && requestInfo) {
const resource =
requestInfo.resource instanceof URL
? requestInfo.resource.toString()
: requestInfo.resource

customInterceptor.onResponse(
requestId,
response, // Pass the original response
resource,
requestInfo.config
)
}

requestMap.delete(requestId)
})
.catch((error: unknown) => {
errorHandler(error, requestId, customInterceptor)
// Start processing the response body without waiting. A non-JSON or
// empty body is not an error — only the request lifecycle matters here.
clonedResponse
.json()
.catch(() => undefined)
.finally(() => {
notifyResponse(requestId, response, customInterceptor)
})

// Return the original response
Expand Down