189 lines
5.2 KiB
TypeScript
189 lines
5.2 KiB
TypeScript
import { ref, onMounted } from 'vue';
|
|
import { DEFAULT_CHAIN_ID } from '@/config';
|
|
import { resolveGraphqlEndpoint } from '@/utils/graphqlRetry';
|
|
import { KRAIKEN_LIB_VERSION, STACK_META_ID } from 'kraiken-lib/version';
|
|
|
|
const WEB_APP_VERSION = __APP_VERSION__;
|
|
|
|
type GraphqlError = { message?: string | null };
|
|
type GraphqlResponse<T> = {
|
|
data?: T;
|
|
errors?: GraphqlError[];
|
|
};
|
|
|
|
interface StackMetaRecord {
|
|
contractVersion?: number | null;
|
|
ponderVersion?: string | null;
|
|
kraikenLibVersion?: number | null;
|
|
updatedAt?: string | null;
|
|
}
|
|
|
|
interface StackMetaResult {
|
|
stackMeta?: StackMetaRecord | null;
|
|
}
|
|
|
|
export interface StackVersions {
|
|
contractVersion: number | null;
|
|
ponderVersion: string | null;
|
|
indexerLibVersion: number | null;
|
|
kraikenLibVersion: number;
|
|
webAppVersion: string;
|
|
updatedAt: bigint | null;
|
|
}
|
|
|
|
export interface VersionStatus {
|
|
isValid: boolean;
|
|
error?: string;
|
|
contractVersion?: number;
|
|
indexerLibVersion?: number;
|
|
ponderVersion?: string | null;
|
|
libVersion: number;
|
|
webAppVersion: string;
|
|
}
|
|
|
|
const STACK_META_QUERY = `
|
|
query StackMeta($id: String!) {
|
|
stackMeta(id: $id) {
|
|
contractVersion
|
|
ponderVersion
|
|
kraikenLibVersion
|
|
updatedAt
|
|
}
|
|
}
|
|
`;
|
|
|
|
const versionStatus = ref<VersionStatus>({
|
|
isValid: true,
|
|
libVersion: KRAIKEN_LIB_VERSION,
|
|
webAppVersion: WEB_APP_VERSION,
|
|
});
|
|
|
|
const stackVersions = ref<StackVersions>({
|
|
contractVersion: null,
|
|
ponderVersion: null,
|
|
indexerLibVersion: null,
|
|
kraikenLibVersion: KRAIKEN_LIB_VERSION,
|
|
webAppVersion: WEB_APP_VERSION,
|
|
updatedAt: null,
|
|
});
|
|
|
|
const isChecking = ref(false);
|
|
const hasChecked = ref(false);
|
|
|
|
function formatError(error: unknown): string {
|
|
if (error instanceof Error && error.message) {
|
|
return error.message;
|
|
}
|
|
if (typeof error === 'string' && error.length > 0) {
|
|
return error;
|
|
}
|
|
return 'Failed to load stack version metadata';
|
|
}
|
|
|
|
function sanitizeNumber(value: unknown): number | null {
|
|
const parsed = typeof value === 'number' ? value : Number(value ?? Number.NaN);
|
|
return Number.isFinite(parsed) ? parsed : null;
|
|
}
|
|
|
|
export function useVersionCheck() {
|
|
async function checkVersions(graphqlEndpointOverride?: string) {
|
|
if (isChecking.value) return versionStatus.value;
|
|
|
|
isChecking.value = true;
|
|
|
|
try {
|
|
const endpoint = resolveGraphqlEndpoint(DEFAULT_CHAIN_ID, graphqlEndpointOverride);
|
|
const response = await fetch(endpoint, {
|
|
method: 'POST',
|
|
headers: { 'content-type': 'application/json' },
|
|
body: JSON.stringify({
|
|
query: STACK_META_QUERY,
|
|
variables: { id: STACK_META_ID },
|
|
}),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`GraphQL stackMeta request failed (HTTP ${response.status})`);
|
|
}
|
|
|
|
const payload = (await response.json()) as GraphqlResponse<StackMetaResult>;
|
|
|
|
const apiErrors = payload.errors?.filter(err => err?.message);
|
|
if (apiErrors && apiErrors.length > 0) {
|
|
throw new Error(apiErrors.map(err => err.message).join(', '));
|
|
}
|
|
|
|
const meta = payload.data?.stackMeta;
|
|
if (!meta) {
|
|
throw new Error('Stack metadata unavailable (stackMeta query returned null)');
|
|
}
|
|
|
|
const contractVersion = sanitizeNumber(meta.contractVersion);
|
|
const ponderVersion = meta.ponderVersion ?? null;
|
|
const indexerLibVersion = sanitizeNumber(meta.kraikenLibVersion);
|
|
const updatedAt = meta.updatedAt ? BigInt(meta.updatedAt) : null;
|
|
|
|
const libMismatch = indexerLibVersion !== null && indexerLibVersion !== KRAIKEN_LIB_VERSION;
|
|
|
|
stackVersions.value = {
|
|
contractVersion,
|
|
ponderVersion,
|
|
indexerLibVersion,
|
|
kraikenLibVersion: KRAIKEN_LIB_VERSION,
|
|
webAppVersion: WEB_APP_VERSION,
|
|
updatedAt,
|
|
};
|
|
|
|
versionStatus.value = {
|
|
isValid: !libMismatch,
|
|
error: libMismatch
|
|
? `Stack mismatch: Ponder reports kraiken-lib v${indexerLibVersion} (frontend expects v${KRAIKEN_LIB_VERSION}). Restart the stack to rebuild containers.`
|
|
: undefined,
|
|
contractVersion: contractVersion ?? undefined,
|
|
indexerLibVersion: indexerLibVersion ?? undefined,
|
|
ponderVersion,
|
|
libVersion: KRAIKEN_LIB_VERSION,
|
|
webAppVersion: WEB_APP_VERSION,
|
|
};
|
|
} catch (error) {
|
|
versionStatus.value = {
|
|
isValid: false,
|
|
error: formatError(error),
|
|
contractVersion: stackVersions.value.contractVersion ?? undefined,
|
|
indexerLibVersion: stackVersions.value.indexerLibVersion ?? undefined,
|
|
ponderVersion: stackVersions.value.ponderVersion,
|
|
libVersion: KRAIKEN_LIB_VERSION,
|
|
webAppVersion: WEB_APP_VERSION,
|
|
};
|
|
} finally {
|
|
hasChecked.value = true;
|
|
isChecking.value = false;
|
|
}
|
|
|
|
return versionStatus.value;
|
|
}
|
|
|
|
return {
|
|
versionStatus,
|
|
stackVersions,
|
|
checkVersions,
|
|
isChecking,
|
|
hasChecked,
|
|
};
|
|
}
|
|
|
|
export function useVersionCheckOnMount(graphqlUrl?: string) {
|
|
const { versionStatus, stackVersions, checkVersions, isChecking, hasChecked } = useVersionCheck();
|
|
|
|
onMounted(async () => {
|
|
await checkVersions(graphqlUrl);
|
|
});
|
|
|
|
return {
|
|
versionStatus,
|
|
stackVersions,
|
|
isChecking,
|
|
hasChecked,
|
|
checkVersions,
|
|
};
|
|
}
|