format code and no semi
parent
eba65bd654
commit
87611700c3
|
|
@ -2,3 +2,4 @@ trailingComma: 'es5'
|
|||
tabWidth: 2
|
||||
semi: false
|
||||
singleQuote: true
|
||||
printWidth: 100
|
||||
|
|
@ -3,11 +3,7 @@ import classes from '@/styles/Header.module.css'
|
|||
import config from '@/uptime.config'
|
||||
|
||||
export default function Header() {
|
||||
const linkToElement = (link: {
|
||||
label: string
|
||||
link: string
|
||||
highlight?: boolean
|
||||
}) => {
|
||||
const linkToElement = (link: { label: string; link: string; highlight?: boolean }) => {
|
||||
return (
|
||||
<a
|
||||
key={link.label}
|
||||
|
|
@ -46,9 +42,7 @@ export default function Header() {
|
|||
</Group>
|
||||
|
||||
<Group gap={5} hiddenFrom="sm">
|
||||
{config.page.links
|
||||
.filter((link) => link.highlight)
|
||||
.map(linkToElement)}
|
||||
{config.page.links.filter((link) => link.highlight).map(linkToElement)}
|
||||
</Group>
|
||||
</Container>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -31,21 +31,17 @@ export default function MonitorDetail({
|
|||
{monitor.name}
|
||||
</Text>
|
||||
<Text mt="sm" fw={700}>
|
||||
No data available, please make sure you have deployed your workers
|
||||
with latest config and check your worker status!
|
||||
No data available, please make sure you have deployed your workers with latest config and
|
||||
check your worker status!
|
||||
</Text>
|
||||
</>
|
||||
)
|
||||
|
||||
const statusIcon =
|
||||
state.incident[monitor.id].slice(-1)[0].end === undefined ? (
|
||||
<IconAlertCircle
|
||||
style={{ width: '1.25em', height: '1.25em', color: '#b91c1c' }}
|
||||
/>
|
||||
<IconAlertCircle style={{ width: '1.25em', height: '1.25em', color: '#b91c1c' }} />
|
||||
) : (
|
||||
<IconCircleCheck
|
||||
style={{ width: '1.25em', height: '1.25em', color: '#059669' }}
|
||||
/>
|
||||
<IconCircleCheck style={{ width: '1.25em', height: '1.25em', color: '#059669' }} />
|
||||
)
|
||||
|
||||
let totalTime = Date.now() / 1000 - state.incident[monitor.id][0].start[0]
|
||||
|
|
@ -56,26 +52,15 @@ export default function MonitorDetail({
|
|||
|
||||
console.log(totalTime)
|
||||
console.log(downTime)
|
||||
const uptimePercent = (
|
||||
((totalTime - downTime) / totalTime) *
|
||||
100
|
||||
).toPrecision(4)
|
||||
const uptimePercent = (((totalTime - downTime) / totalTime) * 100).toPrecision(4)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<Text
|
||||
mt="sm"
|
||||
fw={700}
|
||||
style={{ display: 'inline-flex', alignItems: 'center' }}
|
||||
>
|
||||
<Text mt="sm" fw={700} style={{ display: 'inline-flex', alignItems: 'center' }}>
|
||||
{statusIcon} {monitor.name}
|
||||
</Text>
|
||||
<Text
|
||||
mt="sm"
|
||||
fw={700}
|
||||
style={{ display: 'inline', color: getColor(uptimePercent, true) }}
|
||||
>
|
||||
<Text mt="sm" fw={700} style={{ display: 'inline', color: getColor(uptimePercent, true) }}>
|
||||
Overall: {uptimePercent}%
|
||||
</Text>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,13 +2,7 @@ import { MonitorState, MonitorTarget } from '@/uptime.types'
|
|||
import { Card, Center, Divider } from '@mantine/core'
|
||||
import MonitorDetail from './MonitorDetail'
|
||||
|
||||
export default function MonitorList({
|
||||
config,
|
||||
state,
|
||||
}: {
|
||||
config: any
|
||||
state: MonitorState
|
||||
}) {
|
||||
export default function MonitorList({ config, state }: { config: any; state: MonitorState }) {
|
||||
return (
|
||||
<Center>
|
||||
<Card
|
||||
|
|
|
|||
|
|
@ -7,18 +7,14 @@ export default function OverallStatus({
|
|||
state: { overallUp: number; overallDown: number; lastUpdate: number }
|
||||
}) {
|
||||
let statusString = ''
|
||||
let icon = (
|
||||
<IconAlertCircle style={{ width: 64, height: 64, color: '#b91c1c' }} />
|
||||
)
|
||||
let icon = <IconAlertCircle style={{ width: 64, height: 64, color: '#b91c1c' }} />
|
||||
if (state.overallUp === 0 && state.overallDown === 0) {
|
||||
statusString = 'No data yet'
|
||||
} else if (state.overallUp === 0) {
|
||||
statusString = 'All systems not operational'
|
||||
} else if (state.overallDown === 0) {
|
||||
statusString = 'All systems operational'
|
||||
icon = (
|
||||
<IconCircleCheck style={{ width: 64, height: 64, color: '#059669' }} />
|
||||
)
|
||||
icon = <IconCircleCheck style={{ width: 64, height: 64, color: '#059669' }} />
|
||||
} else {
|
||||
statusString = `Some systems not operational (${state.overallDown} out of ${
|
||||
state.overallUp + state.overallDown
|
||||
|
|
@ -31,11 +27,7 @@ export default function OverallStatus({
|
|||
<Title mt="sm" style={{ textAlign: 'center' }} order={1}>
|
||||
{statusString}
|
||||
</Title>
|
||||
<Title
|
||||
mt="sm"
|
||||
style={{ textAlign: 'center', color: '#70778c' }}
|
||||
order={5}
|
||||
>
|
||||
<Title mt="sm" style={{ textAlign: 'center', color: '#70778c' }} order={5}>
|
||||
Last updated on:{' '}
|
||||
{`${new Date(state.lastUpdate * 1000).toLocaleString()} (${
|
||||
Math.round(Date.now() / 1000) - state.lastUpdate
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ export default function Home({ state }: { state: MonitorState }) {
|
|||
{state === undefined ? (
|
||||
<Center>
|
||||
<Text fw={700}>
|
||||
Monitor State is not defined now, please check your worker's
|
||||
status and KV binding!
|
||||
Monitor State is not defined now, please check your worker's status and KV
|
||||
binding!
|
||||
</Text>
|
||||
</Center>
|
||||
) : (
|
||||
|
|
@ -64,10 +64,7 @@ export async function getServerSideProps() {
|
|||
const { UPTIMEFLARE_STATE } = process.env as unknown as {
|
||||
UPTIMEFLARE_STATE: KVNamespace
|
||||
}
|
||||
const state = (await UPTIMEFLARE_STATE?.get(
|
||||
'state',
|
||||
'json'
|
||||
)) as unknown as MonitorState
|
||||
const state = (await UPTIMEFLARE_STATE?.get('state', 'json')) as unknown as MonitorState
|
||||
|
||||
return { props: { state } }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@
|
|||
height: rem(56px);
|
||||
margin-bottom: rem(120px);
|
||||
background-color: var(--mantine-color-body);
|
||||
border-bottom: rem(1px) solid
|
||||
light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
border-bottom: rem(1px) solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
}
|
||||
|
||||
.inner {
|
||||
|
|
@ -24,10 +23,7 @@
|
|||
font-weight: 500;
|
||||
|
||||
@mixin hover {
|
||||
background-color: light-dark(
|
||||
var(--mantine-color-gray-0),
|
||||
var(--mantine-color-dark-6)
|
||||
);
|
||||
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme] &[data-active] {
|
||||
|
|
|
|||
|
|
@ -10,9 +10,7 @@ const config = {
|
|||
],
|
||||
},
|
||||
callback: async (statusChangeMsg: string) => {
|
||||
await fetch(
|
||||
'https://server.lyc8503.site/wepush?key=wepushkey&msg=' + statusChangeMsg
|
||||
)
|
||||
await fetch('https://server.lyc8503.site/wepush?key=wepushkey&msg=' + statusChangeMsg)
|
||||
},
|
||||
monitors: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,45 +1,47 @@
|
|||
import { connect } from 'cloudflare:sockets';
|
||||
import config from '../../uptime.config';
|
||||
import { fetchTimeout, getWorkerLocation, withTimeout } from './util';
|
||||
import { MonitorState, MonitorTarget } from '../../uptime.types';
|
||||
import { connect } from 'cloudflare:sockets'
|
||||
import config from '../../uptime.config'
|
||||
import { fetchTimeout, getWorkerLocation, withTimeout } from './util'
|
||||
import { MonitorState, MonitorTarget } from '../../uptime.types'
|
||||
|
||||
export interface Env {
|
||||
UPTIMEFLARE_STATE: KVNamespace;
|
||||
UPTIMEFLARE_STATE: KVNamespace
|
||||
}
|
||||
|
||||
async function getStatus(monitor: MonitorTarget): Promise<{ ping: number; up: boolean; err: string }> {
|
||||
async function getStatus(
|
||||
monitor: MonitorTarget
|
||||
): Promise<{ ping: number; up: boolean; err: string }> {
|
||||
let status = {
|
||||
ping: 0,
|
||||
up: false,
|
||||
err: 'Unknown',
|
||||
};
|
||||
}
|
||||
|
||||
const startTime = Date.now();
|
||||
const startTime = Date.now()
|
||||
|
||||
if (monitor.method === 'TCP_PING') {
|
||||
// TCP port endpoint monitor
|
||||
// TODO: TCP timeout
|
||||
try {
|
||||
const [hostname, port] = monitor.target.split(':');
|
||||
const [hostname, port] = monitor.target.split(':')
|
||||
|
||||
// Write "PING\n"
|
||||
const socket = connect({ hostname: hostname, port: Number(port) });
|
||||
const writer = socket.writable.getWriter();
|
||||
await writer.write(new TextEncoder().encode('PING\n'));
|
||||
const socket = connect({ hostname: hostname, port: Number(port) })
|
||||
const writer = socket.writable.getWriter()
|
||||
await writer.write(new TextEncoder().encode('PING\n'))
|
||||
// Can't do this: await socket.close()
|
||||
|
||||
// https://github.com/cloudflare/workerd/issues/1305
|
||||
await withTimeout(monitor.timeout || 10000, socket.closed);
|
||||
await withTimeout(monitor.timeout || 10000, socket.closed)
|
||||
|
||||
console.log(`${monitor.name} connected to ${monitor.target}`);
|
||||
console.log(`${monitor.name} connected to ${monitor.target}`)
|
||||
|
||||
status.ping = Date.now() - startTime;
|
||||
status.up = true;
|
||||
status.err = '';
|
||||
status.ping = Date.now() - startTime
|
||||
status.up = true
|
||||
status.err = ''
|
||||
} catch (e: Error | any) {
|
||||
console.log(`${monitor.name} errored with ${e.name}: ${e.message}`);
|
||||
status.up = false;
|
||||
status.err = e.name + ': ' + e.message;
|
||||
console.log(`${monitor.name} errored with ${e.name}: ${e.message}`)
|
||||
status.up = false
|
||||
status.err = e.name + ': ' + e.message
|
||||
}
|
||||
} else {
|
||||
// HTTP endpoint monitor
|
||||
|
|
@ -53,82 +55,86 @@ async function getStatus(monitor: MonitorTarget): Promise<{ ping: number; up: bo
|
|||
'100-599': -1, // Don't cache any status code, from https://developers.cloudflare.com/workers/runtime-apis/request/#requestinitcfproperties
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
console.log(`${monitor.name} responded with ${response.status}`);
|
||||
status.ping = Date.now() - startTime;
|
||||
console.log(`${monitor.name} responded with ${response.status}`)
|
||||
status.ping = Date.now() - startTime
|
||||
|
||||
if (monitor.expectedCodes) {
|
||||
if (!monitor.expectedCodes.includes(response.status)) {
|
||||
status.up = false;
|
||||
status.err = `Expected codes: ${JSON.stringify(monitor.expectedCodes)}, Got: ${response.status}`;
|
||||
return status;
|
||||
status.up = false
|
||||
status.err = `Expected codes: ${JSON.stringify(monitor.expectedCodes)}, Got: ${
|
||||
response.status
|
||||
}`
|
||||
return status
|
||||
}
|
||||
} else {
|
||||
if (response.status < 200 || response.status > 299) {
|
||||
status.up = false;
|
||||
status.err = `Expected codes: 2xx, Got: ${response.status}`;
|
||||
return status;
|
||||
status.up = false
|
||||
status.err = `Expected codes: 2xx, Got: ${response.status}`
|
||||
return status
|
||||
}
|
||||
}
|
||||
|
||||
if (monitor.responseKeyword) {
|
||||
const responseBody = await response.text();
|
||||
const responseBody = await response.text()
|
||||
if (!responseBody.includes(monitor.responseKeyword)) {
|
||||
status.up = false;
|
||||
status.err = "HTTP response doesn't contain the configured keyword";
|
||||
return status;
|
||||
status.up = false
|
||||
status.err = "HTTP response doesn't contain the configured keyword"
|
||||
return status
|
||||
}
|
||||
}
|
||||
|
||||
status.up = true;
|
||||
status.err = '';
|
||||
status.up = true
|
||||
status.err = ''
|
||||
} catch (e: any) {
|
||||
console.log(`${monitor.name} errored with ${e.name}: ${e.message}`);
|
||||
console.log(`${monitor.name} errored with ${e.name}: ${e.message}`)
|
||||
if (e.name === 'AbortError') {
|
||||
status.ping = monitor.timeout || 10000;
|
||||
status.up = false;
|
||||
status.err = `Timeout after ${status.ping}ms`;
|
||||
status.ping = monitor.timeout || 10000
|
||||
status.up = false
|
||||
status.err = `Timeout after ${status.ping}ms`
|
||||
} else {
|
||||
status.up = false;
|
||||
status.err = e.name + ': ' + e.message;
|
||||
status.up = false
|
||||
status.err = e.name + ': ' + e.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
return status
|
||||
}
|
||||
|
||||
export default {
|
||||
async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext): Promise<void> {
|
||||
const workerLocation = (await getWorkerLocation()) || 'ERROR';
|
||||
console.log(`Running scheduled event on ${workerLocation}...`);
|
||||
const workerLocation = (await getWorkerLocation()) || 'ERROR'
|
||||
console.log(`Running scheduled event on ${workerLocation}...`)
|
||||
|
||||
// Read state, set init state if it doesn't exist
|
||||
let state =
|
||||
((await env.UPTIMEFLARE_STATE.get('state', { type: 'json' })) as unknown as MonitorState) ||
|
||||
((await env.UPTIMEFLARE_STATE.get('state', {
|
||||
type: 'json',
|
||||
})) as unknown as MonitorState) ||
|
||||
({
|
||||
lastUpdate: 0,
|
||||
overallUp: 0,
|
||||
overallDown: 0,
|
||||
incident: {},
|
||||
latency: {},
|
||||
} as MonitorState);
|
||||
state.overallDown = 0;
|
||||
state.overallUp = 0;
|
||||
} as MonitorState)
|
||||
state.overallDown = 0
|
||||
state.overallUp = 0
|
||||
|
||||
// Check each monitor
|
||||
// TODO: callback exception handler
|
||||
// TODO: advanced status check
|
||||
// TODO: concurrent status check
|
||||
for (const monitor of config.monitors) {
|
||||
console.log(`[${workerLocation}] Checking ${monitor.name}...`);
|
||||
console.log(`[${workerLocation}] Checking ${monitor.name}...`)
|
||||
|
||||
const status = await getStatus(monitor);
|
||||
const currentTimeSecond = Math.round(Date.now() / 1000);
|
||||
const status = await getStatus(monitor)
|
||||
const currentTimeSecond = Math.round(Date.now() / 1000)
|
||||
|
||||
// Update counters
|
||||
status.up ? state.overallUp++ : state.overallDown++;
|
||||
status.up ? state.overallUp++ : state.overallDown++
|
||||
|
||||
// Update incidents
|
||||
|
||||
|
|
@ -139,21 +145,23 @@ export default {
|
|||
end: currentTimeSecond,
|
||||
error: ['dummy'],
|
||||
},
|
||||
];
|
||||
]
|
||||
// Then lastIncident here must not be undefined
|
||||
const lastIncident = state.incident[monitor.id].slice(-1)[0];
|
||||
const timeString = new Date().toLocaleString(config.dateLocale, { timeZone: config.timezone });
|
||||
const lastIncident = state.incident[monitor.id].slice(-1)[0]
|
||||
const timeString = new Date().toLocaleString(config.dateLocale, {
|
||||
timeZone: config.timezone,
|
||||
})
|
||||
|
||||
if (status.up) {
|
||||
// Current status is up
|
||||
// close existing incident if any
|
||||
if (lastIncident.end === undefined) {
|
||||
lastIncident.end = currentTimeSecond;
|
||||
lastIncident.end = currentTimeSecond
|
||||
await config.callback(
|
||||
`✔️${monitor.name} came back up at ${timeString} after ${Math.round(
|
||||
(lastIncident.end - lastIncident.start.slice(-1)[0]) / 60,
|
||||
)} minutes of downtime`,
|
||||
);
|
||||
(lastIncident.end - lastIncident.start.slice(-1)[0]) / 60
|
||||
)} minutes of downtime`
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// Current status is down
|
||||
|
|
@ -163,13 +171,20 @@ export default {
|
|||
start: [currentTimeSecond],
|
||||
end: undefined,
|
||||
error: [status.err],
|
||||
});
|
||||
await config.callback(`❌${monitor.name} went down at ${timeString} with error ${status.err}`);
|
||||
} else if (lastIncident.end === undefined && lastIncident.error.slice(-1)[0] !== status.err) {
|
||||
})
|
||||
await config.callback(
|
||||
`❌${monitor.name} went down at ${timeString} with error ${status.err}`
|
||||
)
|
||||
} else if (
|
||||
lastIncident.end === undefined &&
|
||||
lastIncident.error.slice(-1)[0] !== status.err
|
||||
) {
|
||||
// append if the error message changes
|
||||
lastIncident.start.push(currentTimeSecond);
|
||||
lastIncident.error.push(status.err);
|
||||
await config.callback(`❌${monitor.name} is still down at ${timeString} with error ${status.err}`);
|
||||
lastIncident.start.push(currentTimeSecond)
|
||||
lastIncident.error.push(status.err)
|
||||
await config.callback(
|
||||
`❌${monitor.name} is still down at ${timeString} with error ${status.err}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -177,30 +192,30 @@ export default {
|
|||
let latencyLists = state.latency[monitor.id] || {
|
||||
recent: [],
|
||||
all: [],
|
||||
};
|
||||
}
|
||||
|
||||
const record = {
|
||||
loc: workerLocation,
|
||||
ping: status.ping,
|
||||
time: currentTimeSecond,
|
||||
};
|
||||
latencyLists.recent.push(record);
|
||||
}
|
||||
latencyLists.recent.push(record)
|
||||
if (latencyLists.all.length === 0 || currentTimeSecond - latencyLists.all[0].time > 60 * 60) {
|
||||
latencyLists.all.push(record);
|
||||
latencyLists.all.push(record)
|
||||
}
|
||||
|
||||
// discard old data
|
||||
while (latencyLists.recent[0]?.time < currentTimeSecond - 12 * 60 * 60) {
|
||||
latencyLists.recent.shift();
|
||||
latencyLists.recent.shift()
|
||||
}
|
||||
while (latencyLists.all[0]?.time < currentTimeSecond - 90 * 24 * 60 * 60) {
|
||||
latencyLists.all.shift();
|
||||
latencyLists.all.shift()
|
||||
}
|
||||
state.latency[monitor.id] = latencyLists;
|
||||
state.latency[monitor.id] = latencyLists
|
||||
}
|
||||
|
||||
// Update state
|
||||
state.lastUpdate = Math.round(Date.now() / 1000);
|
||||
await env.UPTIMEFLARE_STATE.put('state', JSON.stringify(state));
|
||||
state.lastUpdate = Math.round(Date.now() / 1000)
|
||||
await env.UPTIMEFLARE_STATE.put('state', JSON.stringify(state))
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,29 @@
|
|||
async function getWorkerLocation() {
|
||||
const res = await fetch('https://cloudflare.com/cdn-cgi/trace');
|
||||
const text = await res.text();
|
||||
const res = await fetch('https://cloudflare.com/cdn-cgi/trace')
|
||||
const text = await res.text()
|
||||
|
||||
const colo = /^colo=(.*)$/m.exec(text)?.[1];
|
||||
return colo;
|
||||
const colo = /^colo=(.*)$/m.exec(text)?.[1]
|
||||
return colo
|
||||
}
|
||||
|
||||
const fetchTimeout = (
|
||||
url: string,
|
||||
ms: number,
|
||||
{ signal, ...options }: RequestInit<RequestInitCfProperties> | undefined = {},
|
||||
{ signal, ...options }: RequestInit<RequestInitCfProperties> | undefined = {}
|
||||
): Promise<Response> => {
|
||||
const controller = new AbortController();
|
||||
const promise = fetch(url, { signal: controller.signal, ...options });
|
||||
if (signal) signal.addEventListener('abort', () => controller.abort());
|
||||
const timeout = setTimeout(() => controller.abort(), ms);
|
||||
return promise.finally(() => clearTimeout(timeout));
|
||||
};
|
||||
|
||||
function withTimeout<T>(millis: number, promise: Promise<T>): Promise<T> {
|
||||
const timeout = new Promise<T>((resolve, reject) => setTimeout(() => reject(new Error(`Promise timed out after ${millis}ms`)), millis));
|
||||
|
||||
return Promise.race([promise, timeout]);
|
||||
const controller = new AbortController()
|
||||
const promise = fetch(url, { signal: controller.signal, ...options })
|
||||
if (signal) signal.addEventListener('abort', () => controller.abort())
|
||||
const timeout = setTimeout(() => controller.abort(), ms)
|
||||
return promise.finally(() => clearTimeout(timeout))
|
||||
}
|
||||
|
||||
export { getWorkerLocation, fetchTimeout, withTimeout };
|
||||
function withTimeout<T>(millis: number, promise: Promise<T>): Promise<T> {
|
||||
const timeout = new Promise<T>((resolve, reject) =>
|
||||
setTimeout(() => reject(new Error(`Promise timed out after ${millis}ms`)), millis)
|
||||
)
|
||||
|
||||
return Promise.race([promise, timeout])
|
||||
}
|
||||
|
||||
export { getWorkerLocation, fetchTimeout, withTimeout }
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@
|
|||
|
||||
/* Language and Environment */
|
||||
"target": "es2021" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||
"lib": ["es2021"] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
|
||||
"lib": [
|
||||
"es2021"
|
||||
] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
|
||||
"jsx": "react" /* Specify what JSX code is generated. */,
|
||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
|
|
@ -31,7 +33,9 @@
|
|||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
|
||||
"types": ["@cloudflare/workers-types"] /* Specify type package names to be included without being referenced in a source file. */,
|
||||
"types": [
|
||||
"@cloudflare/workers-types"
|
||||
] /* Specify type package names to be included without being referenced in a source file. */,
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
"resolveJsonModule": true /* Enable importing .json files */,
|
||||
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
|
||||
|
|
|
|||
Loading…
Reference in New Issue