| 823 | } |
| 824 | |
| 825 | function formatAge(age: number): string { |
| 826 | if (age < 1000 * 60) { |
| 827 | const seconds = Math.round(age / 1000); |
| 828 | return `${seconds} second${seconds === 1 ? "" : "s"} ago`; |
| 829 | } |
| 830 | if (age < 1000 * 60 * 60) { |
| 831 | const minutes = Math.round(age / 1000 / 60); |
| 832 | return `${minutes} minute${minutes === 1 ? "" : "s"} ago`; |
| 833 | } |
| 834 | if (age < 1000 * 60 * 60 * 12) { |
| 835 | const hours = Math.round(age / 1000 / 60 / 60); |
| 836 | return `${hours} hour${hours === 1 ? "" : "s"} ago`; |
| 837 | } |
| 838 | return `at ${new Date(Date.now() - age).toLocaleString("sv")}`; |
| 839 | } |
| 840 | |
| 841 | async function readCacheFile(sourceRoot: string, path: string): Promise<string> { |
| 842 | const fullPath = join(sourceRoot, ".observablehq", "cache", path); |