75 lines
2.7 KiB
TypeScript
75 lines
2.7 KiB
TypeScript
const fullCommitShaPattern = /^[0-9a-f]{40,64}$/iu;
|
|
|
|
type VersionStorage = Pick<Storage, 'getItem' | 'setItem'>;
|
|
|
|
export type DeploymentVersionCheckerOptions = {
|
|
currentCommitSha: string;
|
|
versionUrl: string;
|
|
fetchVersion?: typeof fetch;
|
|
storage?: VersionStorage;
|
|
onVersionChanged: (availableCommitSha: string) => void;
|
|
};
|
|
|
|
export const deploymentVersionAssetSource = (buildCommitSha: string): string =>
|
|
`${JSON.stringify({ commitSha: buildCommitSha })}\n`;
|
|
|
|
export const parseDeploymentCommitSha = (payload: unknown): string | null => {
|
|
if (!payload || typeof payload !== 'object' || !('commitSha' in payload)) return null;
|
|
const commitSha = String(payload.commitSha).trim().toLowerCase();
|
|
return fullCommitShaPattern.test(commitSha) ? commitSha : null;
|
|
};
|
|
|
|
const notificationStorageKey = (versionUrl: string, availableCommitSha: string): string =>
|
|
`sammo:deployment-version-notice:${versionUrl}:${availableCommitSha}`;
|
|
|
|
export const createDeploymentVersionChecker = (options: DeploymentVersionCheckerOptions) => {
|
|
const currentCommitSha = options.currentCommitSha.trim().toLowerCase();
|
|
const fetchVersion = options.fetchVersion ?? fetch;
|
|
let inFlight: Promise<void> | null = null;
|
|
let lastNotifiedCommitSha: string | null = null;
|
|
|
|
const wasNotified = (key: string): boolean => {
|
|
try {
|
|
return options.storage?.getItem(key) === '1';
|
|
} catch {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
const rememberNotification = (key: string): void => {
|
|
try {
|
|
options.storage?.setItem(key, '1');
|
|
} catch {
|
|
// Session storage can be unavailable under restrictive browser policies.
|
|
}
|
|
};
|
|
|
|
const run = async (): Promise<void> => {
|
|
if (!fullCommitShaPattern.test(currentCommitSha)) return;
|
|
const response = await fetchVersion(options.versionUrl, {
|
|
cache: 'no-cache',
|
|
});
|
|
if (!response.ok) return;
|
|
const availableCommitSha = parseDeploymentCommitSha(await response.json());
|
|
if (!availableCommitSha || availableCommitSha === currentCommitSha) return;
|
|
|
|
const storageKey = notificationStorageKey(options.versionUrl, availableCommitSha);
|
|
if (lastNotifiedCommitSha === availableCommitSha || wasNotified(storageKey)) return;
|
|
lastNotifiedCommitSha = availableCommitSha;
|
|
rememberNotification(storageKey);
|
|
options.onVersionChanged(availableCommitSha);
|
|
};
|
|
|
|
return {
|
|
check: (): Promise<void> => {
|
|
if (inFlight) return inFlight;
|
|
inFlight = run()
|
|
.catch(() => undefined)
|
|
.finally(() => {
|
|
inFlight = null;
|
|
});
|
|
return inFlight;
|
|
},
|
|
};
|
|
};
|