@nitrogenbuilder/client-core
Version:
Nitrogen Builder Core Client
86 lines (75 loc) • 2.18 kB
text/typescript
export type BatchRequest = {
key: string;
endpoint: string;
params: Record<string, string>;
};
export type BatchResult = {
data: any;
total: number;
totalPages: number;
};
export type BatchTransport = (
requests: BatchRequest[]
) => Promise<Record<string, BatchResult>>;
export class BatchFetchContext {
private requests: BatchRequest[] = [];
private pendingResolvers = new Map<
string,
Array<{ resolve: (value: BatchResult) => void; reject: (reason?: any) => void }>
>();
private flushed = false;
constructor(private transport: BatchTransport) {}
/**
* Called by getData functions to register a request.
* Returns a promise that resolves after flush() completes.
*/
fetch(
endpoint: string,
params: Record<string, string> = {}
): Promise<BatchResult> {
const key = `${endpoint}?${new URLSearchParams(params).toString()}`;
if (this.flushed) {
// Late registration — batch already sent, fall back to individual request
return this.transport([{ key, endpoint, params }]).then(
(r) => r[key]
);
}
// Deduplicate: same endpoint+params shares the same promise resolution
if (!this.pendingResolvers.has(key)) {
this.requests.push({ key, endpoint, params });
this.pendingResolvers.set(key, []);
}
return new Promise((resolve, reject) => {
this.pendingResolvers.get(key)!.push({ resolve, reject });
});
}
/**
* Called after all getData functions have registered their requests.
* Sends a single batch request via the transport and resolves all pending promises.
*/
async flush(): Promise<void> {
this.flushed = true;
if (this.requests.length === 0) return;
try {
const results = await this.transport(this.requests);
for (const [key, resolvers] of this.pendingResolvers) {
const result = results[key];
if (result) {
for (const { resolve } of resolvers) {
resolve(result);
}
} else {
for (const { reject } of resolvers) {
reject(new Error(`No batch result for key: ${key}`));
}
}
}
} catch (error) {
for (const [, resolvers] of this.pendingResolvers) {
for (const { reject } of resolvers) {
reject(error);
}
}
}
}
}