feat(data-loading): load fee schedule catalogs and data from generated JSON payloads instead of bundled Germany TypeScript exports
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
import type { IFeeSchedulePayload } from './feeschedules.types.js';
|
||||
|
||||
export const FEE_SCHEDULE_DATA_URL = 'https://code.foss.global/fin.cx/fee-schedules/raw/branch/main/.onlygit/fee-schedules.json';
|
||||
|
||||
export async function loadFeeSchedulePayload(url = FEE_SCHEDULE_DATA_URL): Promise<IFeeSchedulePayload> {
|
||||
let remoteError: unknown;
|
||||
|
||||
try {
|
||||
return await fetchPayload(url);
|
||||
} catch (error) {
|
||||
remoteError = error;
|
||||
}
|
||||
|
||||
const browserLocalPayload = await loadBrowserLocalPayload();
|
||||
if (browserLocalPayload) {
|
||||
return browserLocalPayload;
|
||||
}
|
||||
|
||||
const nodeLocalPayload = await loadNodeLocalPayload();
|
||||
if (nodeLocalPayload) {
|
||||
return nodeLocalPayload;
|
||||
}
|
||||
|
||||
throw new Error(`Failed to load fee schedule data from ${url}: ${formatError(remoteError)}`);
|
||||
}
|
||||
|
||||
const fetchPayload = async (url: string): Promise<IFeeSchedulePayload> => {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
return parsePayload(await response.json());
|
||||
};
|
||||
|
||||
const loadBrowserLocalPayload = async (): Promise<IFeeSchedulePayload | undefined> => {
|
||||
if (typeof location === 'undefined') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
return await fetchPayload('/.onlygit/fee-schedules.json');
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const loadNodeLocalPayload = async (): Promise<IFeeSchedulePayload | undefined> => {
|
||||
if (!isNodeRuntime()) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const dynamicImport = new Function('specifier', 'return import(specifier)') as (specifier: string) => Promise<any>;
|
||||
const [{ readFile }, { fileURLToPath }, path] = await Promise.all([
|
||||
dynamicImport('node:fs/promises'),
|
||||
dynamicImport('node:url'),
|
||||
dynamicImport('node:path'),
|
||||
]);
|
||||
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
const filePath = path.resolve(currentDir, '../.onlygit/fee-schedules.json');
|
||||
const fileContent = await readFile(filePath, 'utf8');
|
||||
return parsePayload(JSON.parse(fileContent));
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const parsePayload = (payload: unknown): IFeeSchedulePayload => {
|
||||
if (!payload || typeof payload !== 'object') {
|
||||
throw new Error('Fee schedule payload is not an object.');
|
||||
}
|
||||
|
||||
const typedPayload = payload as IFeeSchedulePayload;
|
||||
if (!Array.isArray(typedPayload.catalogs) || !Array.isArray(typedPayload.scheduleData)) {
|
||||
throw new Error('Fee schedule payload is missing catalogs or scheduleData arrays.');
|
||||
}
|
||||
|
||||
return typedPayload;
|
||||
};
|
||||
|
||||
const isNodeRuntime = (): boolean => {
|
||||
return typeof process !== 'undefined' && Boolean(process.versions?.node);
|
||||
};
|
||||
|
||||
const formatError = (error: unknown): string => {
|
||||
return error instanceof Error ? error.message : String(error);
|
||||
};
|
||||
|
||||
export const feeSchedulePayload = await loadFeeSchedulePayload();
|
||||
export const FEE_SCHEDULE_CATALOGS = feeSchedulePayload.catalogs;
|
||||
export const FEE_SCHEDULE_DATA = feeSchedulePayload.scheduleData;
|
||||
export const SUPPORTED_COUNTRY_CODES = FEE_SCHEDULE_CATALOGS.map((catalog) => catalog.countryCode);
|
||||
Reference in New Issue
Block a user