@tanstack/solid-start
Version:
Modern and scalable routing for Solid applications
119 lines (98 loc) • 2.81 kB
text/typescript
const START_PAYLOAD_DATA_ARG = 1
export function encodeSolidStartPayload(args: Array<any>): Array<any> {
if (args.length !== 1 || !isObject(args[0])) {
return args
}
const payload = args[0]
const data = payload.data
const solidPayload = stripUndefined({
method: payload.method,
headers: normalizeHeaders(payload.headers as HeadersInit | undefined),
context: stripUndefined(payload.context),
})
if (isFormDataLike(data)) {
return [solidPayload, normalizeFormData(data)]
}
return [
stripUndefined({
...solidPayload,
data,
}),
]
}
export function decodeSolidStartPayload(args: Array<any>): any {
const payload = args[0]
if (
args.length > START_PAYLOAD_DATA_ARG &&
isObject(payload) &&
isFormDataLike(args[START_PAYLOAD_DATA_ARG])
) {
return {
...payload,
data: normalizeFormData(args[START_PAYLOAD_DATA_ARG]),
}
}
if (args.length === 1 && isFormDataLike(payload)) {
return { data: normalizeFormData(payload), method: 'POST' }
}
return payload
}
function normalizeHeaders(headers: HeadersInit | undefined) {
if (!headers) {
return undefined
}
const normalized: Record<string, string> = {}
new Headers(headers).forEach((value, key) => {
normalized[key] = value
})
return normalized
}
interface FormDataLike {
append: (name: string, value: unknown) => void
entries: () => IterableIterator<[string, FormDataEntryValue]>
get: (name: string) => FormDataEntryValue | null
}
function isFormDataLike(value: unknown): value is FormDataLike {
return (
typeof FormData !== 'undefined' &&
isObject(value) &&
typeof value.append === 'function' &&
typeof value.entries === 'function' &&
typeof value.get === 'function'
)
}
function normalizeFormData(value: FormDataLike) {
if (value instanceof FormData) {
return value
}
const normalized = new FormData()
for (const [name, entry] of value.entries()) {
normalized.append(name, entry)
}
return normalized
}
function stripUndefined<T>(value: T): T {
if (Array.isArray(value)) {
return value.map((item) => stripUndefined(item)) as T
}
if (!isPlainObject(value)) {
return value
}
const stripped: Record<string, unknown> = {}
for (const [key, nestedValue] of Object.entries(value)) {
if (nestedValue !== undefined) {
stripped[key] = stripUndefined(nestedValue)
}
}
return stripped as T
}
function isObject(value: unknown): value is Record<string, unknown> {
return !!value && typeof value === 'object'
}
function isPlainObject(value: unknown): value is Record<string, unknown> {
if (!isObject(value)) {
return false
}
const prototype = Object.getPrototypeOf(value)
return prototype === Object.prototype || prototype === null
}