export opaque type BackingFormData = {
prefix: string,
referencedEntries: null | Array<[string, FormDataEntryValue]>,
keyPointer: number,
stringChunks: null | Map<string, string>,
streamChunks: null | Map<string, Array<string>>,
blobs: null | Map<string, Blob>,
};
export function appendBackingEntry(
backingStore: BackingFormData,
value: FormDataEntryValue,
key: string,
): void {
const prefix = backingStore.prefix;
if (!key.startsWith(prefix)) {
return;
}
const namespace = key[prefix.length];
switch (namespace) {
case '_': {
let referencedEntries = backingStore.referencedEntries;
if (referencedEntries === null) {
referencedEntries = backingStore.referencedEntries = [];
}
referencedEntries.push([key, value]);
return;
}
case 'S': {
if (typeof value === 'string') {
let streamChunks = backingStore.streamChunks;
if (streamChunks === null) {
streamChunks = backingStore.streamChunks = new Map();
}
const existing = streamChunks.get(key);
if (existing === undefined) {
streamChunks.set(key, [value]);
} else {
existing.push(value);
}
}
return;
}
}
if (typeof value === 'string') {
let stringChunks = backingStore.stringChunks;
if (stringChunks === null) {
stringChunks = backingStore.stringChunks = new Map();
}
stringChunks.set(key, value);
} else {
let blobs = backingStore.blobs;
if (blobs === null) {
blobs = backingStore.blobs = new Map();
}
blobs.set(key, value);
}
}
export function createBackingFormData(
formData: FormData,
prefix: string,
): BackingFormData {
const backingStore: BackingFormData = {
prefix,
referencedEntries: null,
keyPointer: 0,
stringChunks: null,
streamChunks: null,
blobs: null,
};
formData.forEach(appendBackingEntry.bind(null, backingStore));
return backingStore;
}
export function consumeBackingEntries(
backingStore: BackingFormData,
formPrefix: string,
): FormData {
const data = new FormData();
const entries = backingStore.referencedEntries;
if (entries === null) {
return data;
}
let i = backingStore.keyPointer;
while (i < entries.length) {
const entry = entries[i];
const key = entry[0];
if (!key.startsWith(formPrefix)) {
break;
}
data.append(key.slice(formPrefix.length), entry[1]);
i++;
}
backingStore.keyPointer = i;
return data;
}
export function getBackingString(
backingStore: BackingFormData,
key: string,
): string | void {
const stringChunks = backingStore.stringChunks;
return stringChunks === null ? undefined : stringChunks.get(key);
}
export function getStreamChunks(
backingStore: BackingFormData,
key: string,
): Array<string> | void {
const streamChunks = backingStore.streamChunks;
return streamChunks === null ? undefined : streamChunks.get(key);
}
export function getBackingBlob(
backingStore: BackingFormData,
key: string,
): Blob | void {
const blobs = backingStore.blobs;
return blobs === null ? undefined : blobs.get(key);
}