react-antd-admin-panel
Version:
Modern TypeScript-first React admin panel builder with Ant Design 6
166 lines • 4.74 kB
JavaScript
/**
* Loader - Data pre-fetching orchestrator for page lifecycle
* Manages multiple HTTP requests with dependencies and conditions
*
* @example
* const loader = new Loader()
* .add('user', new Get().target('/api/user'))
* .add('posts', new Get().target('/api/posts'), {
* condition: (data) => data.user?.isAdmin
* })
* .onLoad((data) => console.log('Loaded:', data))
* .execute();
*/
export class Loader {
_requests = [];
_hooks = {};
_parallel = true;
_abortOnError = false;
/**
* Add a request to the loader
*/
add(key, request, options) {
this._requests.push({
key,
request,
required: options?.required ?? false,
condition: options?.condition,
});
return this;
}
/**
* Set whether requests execute in parallel (default) or sequentially
*/
parallel(value = true) {
this._parallel = value;
return this;
}
/**
* Set whether to abort on first error (default: false)
*/
abortOnError(value = true) {
this._abortOnError = value;
return this;
}
/**
* Hook called before any requests are made
*/
onBeforeLoad(callback) {
this._hooks.onBeforeLoad = callback;
return this;
}
/**
* Hook called after all requests complete successfully
*/
onLoad(callback) {
this._hooks.onLoad = callback;
return this;
}
/**
* Hook called after onLoad (useful for side effects)
*/
onAfterLoad(callback) {
this._hooks.onAfterLoad = callback;
return this;
}
/**
* Hook called if any request fails
*/
onError(callback) {
this._hooks.onError = callback;
return this;
}
/**
* Execute all requests with lifecycle management
*/
async execute() {
const data = {};
try {
// Before load hook
if (this._hooks.onBeforeLoad) {
await this._hooks.onBeforeLoad();
}
if (this._parallel) {
// Execute all requests in parallel
await this._executeParallel(data);
}
else {
// Execute requests sequentially (allows conditions to check previous results)
await this._executeSequential(data);
}
// On load hook
if (this._hooks.onLoad) {
await this._hooks.onLoad(data);
}
// After load hook
if (this._hooks.onAfterLoad) {
await this._hooks.onAfterLoad(data);
}
return data;
}
catch (error) {
// Error hook
if (this._hooks.onError) {
await this._hooks.onError(error);
}
throw error;
}
}
/**
* Execute requests in parallel
*/
async _executeParallel(data) {
const promises = this._requests.map(async (req) => {
// Check condition
if (req.condition && !req.condition(data)) {
return;
}
try {
const result = await req.request.execute();
data[req.key] = result;
}
catch (error) {
if (req.required || this._abortOnError) {
throw error;
}
// Non-required request failed, continue
data[req.key] = null;
}
});
await Promise.all(promises);
}
/**
* Execute requests sequentially (allows conditions to depend on previous results)
*/
async _executeSequential(data) {
for (const req of this._requests) {
// Check condition based on already-loaded data
if (req.condition && !req.condition(data)) {
continue;
}
try {
const result = await req.request.execute();
data[req.key] = result;
}
catch (error) {
if (req.required || this._abortOnError) {
throw error;
}
// Non-required request failed, continue
data[req.key] = null;
}
}
}
/**
* Create a loader from a configuration object
*/
static from(config) {
const loader = new Loader();
loader._requests = config.requests;
loader._hooks = config.hooks || {};
loader._parallel = config.parallel ?? true;
loader._abortOnError = config.abortOnError ?? false;
return loader;
}
}
//# sourceMappingURL=Loader.js.map