Merge pull request #11 from Erisa/v2
v2: Rewrite in TypeScript and Hono
This commit is contained in:
commit
268ff94cea
8 changed files with 695 additions and 660 deletions
|
|
@ -2,6 +2,6 @@
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"semi": false,
|
"semi": false,
|
||||||
"trailingComma": "all",
|
"trailingComma": "all",
|
||||||
"tabWidth": 2,
|
"useTabs": true,
|
||||||
"printWidth": 80
|
"printWidth": 80
|
||||||
}
|
}
|
||||||
|
|
|
||||||
6
.vscode/extensions.json
vendored
6
.vscode/extensions.json
vendored
|
|
@ -1,5 +1,3 @@
|
||||||
{
|
{
|
||||||
"recommendations": [
|
"recommendations": ["bungcip.better-toml"]
|
||||||
"bungcip.better-toml"
|
}
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
|
||||||
273
index.js
273
index.js
|
|
@ -1,273 +0,0 @@
|
||||||
let secret
|
|
||||||
|
|
||||||
addEventListener('fetch', (event) => {
|
|
||||||
event.respondWith(handleRequest(event))
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Respond to the request
|
|
||||||
* @param {Event} event
|
|
||||||
*/
|
|
||||||
async function handleRequest(event) {
|
|
||||||
const { request } = event
|
|
||||||
// Set this in your worker's environment. wrangler.toml or cloudflare dashboard
|
|
||||||
if (WORKERLINKS_SECRET === undefined) {
|
|
||||||
return new Response('Secret is not defined. Please add WORKERLINKS_SECRET.')
|
|
||||||
} else {
|
|
||||||
secret = WORKERLINKS_SECRET
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = new URL(request.url).pathname
|
|
||||||
// Trim trailing slash
|
|
||||||
let key = path !== '/' ? path.replace(/\/$/, '') : path
|
|
||||||
let shorturl = new URL(key, request.url).origin
|
|
||||||
|
|
||||||
if (request.method == 'PUT') {
|
|
||||||
return await putLink(
|
|
||||||
request.headers.get('Authorization'),
|
|
||||||
shorturl,
|
|
||||||
key,
|
|
||||||
request.headers.get('URL'),
|
|
||||||
)
|
|
||||||
} else if (request.method == 'POST') {
|
|
||||||
if (key != '/') {
|
|
||||||
return Response.json(
|
|
||||||
{
|
|
||||||
code: '405 Method Not Allowed',
|
|
||||||
message: 'POST not valid for individual keys. Did you mean PUT?',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 405,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
const body = await request.text()
|
|
||||||
|
|
||||||
if (!body) {
|
|
||||||
key = '/' + Math.random().toString(36).slice(5)
|
|
||||||
shorturl = new URL(key, request.url)
|
|
||||||
return await putLink(
|
|
||||||
request.headers.get('Authorization'),
|
|
||||||
shorturl,
|
|
||||||
key,
|
|
||||||
request.headers.get('URL'),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
if (request.headers.get('Authorization') != secret) {
|
|
||||||
return Response.json(
|
|
||||||
{
|
|
||||||
code: '401 Unauthorized',
|
|
||||||
message: 'Unauthorized.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 401,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let json
|
|
||||||
try {
|
|
||||||
json = JSON.parse(body)
|
|
||||||
} catch {
|
|
||||||
return Response.json(
|
|
||||||
{
|
|
||||||
code: '400 Bad Request',
|
|
||||||
message: 'Body must be valid JSON, or none at all.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 400,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const valid = validateBulkBody(json)
|
|
||||||
if (!valid) {
|
|
||||||
return Response.json(
|
|
||||||
{
|
|
||||||
code: '400 Bad Request',
|
|
||||||
message:
|
|
||||||
'Body must be a standard JSON object mapping keys to urls.',
|
|
||||||
example: {
|
|
||||||
'/short': 'https://example.com/really-really-really-long-1',
|
|
||||||
'/other': 'https://subdomain.example.com/and-some-long-path',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ status: 400 },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [key, url] of Object.entries(json)) {
|
|
||||||
await kv.put(key, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Response.json(
|
|
||||||
{
|
|
||||||
message: 'URLs created successfully',
|
|
||||||
entries: Object.entries(json).map(([key, longurl]) => ({
|
|
||||||
key: key.slice(1),
|
|
||||||
shorturl: new URL(key, request.url),
|
|
||||||
longurl,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
{ status: 200 },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else if (request.method == 'GET' || request.method == 'HEAD') {
|
|
||||||
let url = await kv.get(key)
|
|
||||||
if (url == null) {
|
|
||||||
return Response.json(
|
|
||||||
{
|
|
||||||
code: '404 Not Found',
|
|
||||||
message: 'Key does not exist or has not propagated.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 404,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// PLAUSIBLE_HOST should be the full URL to your Plausible Analytics instance
|
|
||||||
// e.g. https://plausible.io/
|
|
||||||
if (typeof PLAUSIBLE_HOST !== 'undefined') {
|
|
||||||
const url = PLAUSIBLE_HOST + 'api/event'
|
|
||||||
const headers = new Headers()
|
|
||||||
headers.append('User-Agent', request.headers.get('User-Agent'))
|
|
||||||
headers.append(
|
|
||||||
'X-Forwarded-For',
|
|
||||||
request.headers.get('X-Forwarded-For'),
|
|
||||||
)
|
|
||||||
headers.append('Content-Type', 'application/json')
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
name: 'pageview',
|
|
||||||
url: request.url,
|
|
||||||
domain: new URL(request.url).hostname,
|
|
||||||
referrer: request.referrer,
|
|
||||||
}
|
|
||||||
event.waitUntil(
|
|
||||||
fetch(url, { method: 'POST', headers, body: JSON.stringify(data) }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const searchParams = new URL(request.url).searchParams
|
|
||||||
url = new URL(url)
|
|
||||||
searchParams.forEach((value, key) => {
|
|
||||||
url.searchParams.append(key, value)
|
|
||||||
})
|
|
||||||
|
|
||||||
return new Response(null, { status: 302, headers: { Location: url } })
|
|
||||||
}
|
|
||||||
} else if (request.method == 'DELETE') {
|
|
||||||
if (request.headers.get('Authorization') != secret) {
|
|
||||||
return Response.json(
|
|
||||||
{
|
|
||||||
code: '401 Unauthorized',
|
|
||||||
message: 'Unauthorized.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 401,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
shorturl = new URL(key, request.url)
|
|
||||||
let url = await kv.get(key)
|
|
||||||
if (url == null) {
|
|
||||||
return Response.json(
|
|
||||||
{
|
|
||||||
code: '404 Not Found',
|
|
||||||
message: 'Key does not exist or has not propagated.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 404,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
await kv.delete(key)
|
|
||||||
return Response.json(
|
|
||||||
{
|
|
||||||
message: 'Short URL deleted succesfully.',
|
|
||||||
key: key.slice(1),
|
|
||||||
shorturl: shorturl,
|
|
||||||
longurl: url,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 200,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Response.json(
|
|
||||||
{
|
|
||||||
code: '405 Method Not Allowed',
|
|
||||||
message:
|
|
||||||
'Unsupported method. Please use one of GET, PUT, POST, DELETE, HEAD.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 405,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateUrl(url) {
|
|
||||||
// quick and dirty validation
|
|
||||||
if (url == '') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
new URL(url)
|
|
||||||
} catch (TypeError) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// zod and other validation libs too
|
|
||||||
function validateBulkBody(body) {
|
|
||||||
// Starting `/` and no ending `/`
|
|
||||||
const keyRe = /^\/.*?[^\/]$/
|
|
||||||
|
|
||||||
if (!body || typeof body !== 'object' || Array.isArray(body)) return false
|
|
||||||
return Object.entries(body).every(
|
|
||||||
([key, url]) => keyRe.test(key) && validateUrl(url),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function putLink(givenSecret, shorturl, key, url) {
|
|
||||||
if (givenSecret != secret) {
|
|
||||||
return Response.json(
|
|
||||||
{
|
|
||||||
code: '401 Unauthorized',
|
|
||||||
message: 'Unauthorized.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 401,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (url == null || !validateUrl(url)) {
|
|
||||||
return Response.json(
|
|
||||||
{
|
|
||||||
code: '400 Bad Request',
|
|
||||||
message: "No valid URL given. Please set a 'URL' header.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 400,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
await kv.put(key, url)
|
|
||||||
return Response.json(
|
|
||||||
{
|
|
||||||
message: 'URL created succesfully.',
|
|
||||||
key: key.slice(1),
|
|
||||||
shorturl: shorturl,
|
|
||||||
longurl: url,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 200,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
38
package.json
38
package.json
|
|
@ -1,19 +1,23 @@
|
||||||
{
|
{
|
||||||
"private": true,
|
"name": "worker-links",
|
||||||
"name": "worker-links",
|
"version": "2.0.0",
|
||||||
"version": "1.0.0",
|
"description": "Simple link shortener for Cloudflare Workers.",
|
||||||
"description": "Simple link shortener for Cloudflare Workers.",
|
"author": "Erisa A",
|
||||||
"main": "index.js",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"format": "prettier --write '**/*.{js,css,json,md}'",
|
"format": "prettier --write '**/*.{ts,js,css,json,md}'",
|
||||||
"deploy": "wrangler publish",
|
"deploy": "wrangler publish",
|
||||||
"dev": "wrangler dev --local",
|
"dev": "wrangler dev",
|
||||||
"addsecret": "wrangler secret put WORKERLINKS_SECRET"
|
"addsecret": "wrangler secret put WORKERLINKS_SECRET",
|
||||||
},
|
"build": "wrangler publish --dry-run --outdir=./dist"
|
||||||
"author": "Erisa A",
|
},
|
||||||
"license": "MIT",
|
"dependencies": {
|
||||||
"devDependencies": {
|
"hono": "^3.0.2",
|
||||||
"prettier": "^2.6.1",
|
"simple-runtypes": "^7.1.3"
|
||||||
"wrangler": "^2.6.2"
|
},
|
||||||
}
|
"devDependencies": {
|
||||||
|
"@cloudflare/workers-types": "^4.20230228.0",
|
||||||
|
"prettier": "^2.8.4",
|
||||||
|
"wrangler": "^2.12.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
264
src/index.ts
Normal file
264
src/index.ts
Normal file
|
|
@ -0,0 +1,264 @@
|
||||||
|
import { Context, Hono } from 'hono'
|
||||||
|
import * as st from 'simple-runtypes'
|
||||||
|
|
||||||
|
type Variables = {
|
||||||
|
path: string
|
||||||
|
key: string
|
||||||
|
shortUrl: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bindings = {
|
||||||
|
WORKERLINKS_SECRET: string
|
||||||
|
KV: KVNamespace
|
||||||
|
kv: KVNamespace
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = st.runtype((v) => {
|
||||||
|
const stringCheck = st.use(st.string(), v)
|
||||||
|
if (!stringCheck.ok) return stringCheck.error
|
||||||
|
|
||||||
|
try {
|
||||||
|
new URL(stringCheck.result)
|
||||||
|
return stringCheck.result
|
||||||
|
} catch {
|
||||||
|
return st.createError('Must be a valid URL')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const bulkValidator = st.dictionary(
|
||||||
|
// Starting `/` and no ending `/`
|
||||||
|
st.string({ match: /^\/.*?[^\/]$/ }),
|
||||||
|
url,
|
||||||
|
)
|
||||||
|
|
||||||
|
const app = new Hono<{ Variables: Variables; Bindings: Bindings }>()
|
||||||
|
|
||||||
|
// store the path, key and short url for reference in requeests
|
||||||
|
// e.g. c.get('key')
|
||||||
|
app.use('*', async (c, next) => {
|
||||||
|
const path = new URL(c.req.url).pathname
|
||||||
|
let key = path !== '/' ? path.replace(/\/$/, '') : path
|
||||||
|
let shortUrl = new URL(key, c.req.url).origin
|
||||||
|
|
||||||
|
c.set('path', path)
|
||||||
|
c.set('key', key)
|
||||||
|
c.set('shortUrl', shortUrl)
|
||||||
|
|
||||||
|
// backwards compat
|
||||||
|
// i could have left env.kv alone, but i like them being CAPITALS.
|
||||||
|
// what can i say?
|
||||||
|
if (c.env.KV == undefined && c.env.kv !== undefined) {
|
||||||
|
c.env.KV = c.env.kv
|
||||||
|
console.warn('WARN: Please change your kv binding to be called KV.')
|
||||||
|
}
|
||||||
|
|
||||||
|
await next()
|
||||||
|
})
|
||||||
|
|
||||||
|
// handle auth
|
||||||
|
app.use('*', async (c, next) => {
|
||||||
|
if (c.env.WORKERLINKS_SECRET === undefined) {
|
||||||
|
return c.text('Secret is not defined. Please add WORKERLINKS_SECRET.')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!['GET', 'HEAD'].includes(c.req.method) &&
|
||||||
|
c.req.headers.get('Authorization') !== c.env.WORKERLINKS_SECRET
|
||||||
|
) {
|
||||||
|
return c.json({ code: '401 Unauthorized', message: 'Unauthorized' }, 401)
|
||||||
|
}
|
||||||
|
|
||||||
|
await next()
|
||||||
|
})
|
||||||
|
|
||||||
|
// retrieve key
|
||||||
|
app.get('*', handleGetHead)
|
||||||
|
|
||||||
|
// same but for HEAD
|
||||||
|
app.head('*', handleGetHead)
|
||||||
|
|
||||||
|
// handle both GET and HEAD
|
||||||
|
async function handleGetHead(c: Context) {
|
||||||
|
// actual logic goes here
|
||||||
|
const urlResult = await c.env.KV.get(c.get('key'))
|
||||||
|
|
||||||
|
if (urlResult == null) {
|
||||||
|
return c.json(
|
||||||
|
{ code: '404 Not Found', message: ' Key does not exist.' },
|
||||||
|
404,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
const searchParams = new URL(c.req.url).searchParams
|
||||||
|
const newUrl = new URL(urlResult)
|
||||||
|
searchParams.forEach((value, key) => {
|
||||||
|
newUrl.searchParams.append(key, value)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (c.env.PLAUSIBLE_HOST !== undefined) {
|
||||||
|
c.executionCtx.waitUntil(sendToPlausible(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.redirect(newUrl.toString(), 302)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete specific key
|
||||||
|
app.delete('*', async (c) => {
|
||||||
|
const urlResult = await c.env.KV.get(c.get('key'))
|
||||||
|
|
||||||
|
if (urlResult == null) {
|
||||||
|
return c.json(
|
||||||
|
{ code: '404 Not Found', message: ' Key does not exist.' },
|
||||||
|
404,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
await c.env.KV.delete(c.get('key'))
|
||||||
|
return c.json({
|
||||||
|
message: 'Short URL deleted succesfully.',
|
||||||
|
key: c.get('key').slice(1),
|
||||||
|
shorturl: c.get('shortUrl'),
|
||||||
|
longurl: urlResult,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// add specific key
|
||||||
|
app.put('*', createLink)
|
||||||
|
|
||||||
|
// add random key
|
||||||
|
app.post('/', async (c) => {
|
||||||
|
const rawBody = await c.req.text()
|
||||||
|
|
||||||
|
if (!rawBody) {
|
||||||
|
c.set('key', '/' + Math.random().toString(36).slice(5))
|
||||||
|
c.set('shortUrl', new URL(c.get('key'), c.req.url).toString())
|
||||||
|
return await createLink(c)
|
||||||
|
} else {
|
||||||
|
// Bulk upload from body
|
||||||
|
|
||||||
|
let json: unknown
|
||||||
|
try {
|
||||||
|
json = JSON.parse(rawBody)
|
||||||
|
} catch {
|
||||||
|
return c.json(
|
||||||
|
{
|
||||||
|
code: '400 Bad Request',
|
||||||
|
message: 'Body must be valid JSON, or none at all.',
|
||||||
|
},
|
||||||
|
400,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = st.use(bulkValidator, json)
|
||||||
|
if (!result.ok) {
|
||||||
|
return c.json(
|
||||||
|
{
|
||||||
|
code: '400 Bad Request',
|
||||||
|
message: 'Body must be a standard JSON object mapping keys to urls.',
|
||||||
|
example: {
|
||||||
|
'/short': 'https://example.com/really-really-really-long-1',
|
||||||
|
'/other': 'https://subdomain.example.com/and-some-long-path',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
400,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, url] of Object.entries(result.result)) {
|
||||||
|
await c.env.KV.put(key, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.json(
|
||||||
|
{
|
||||||
|
message: 'URLs created successfully',
|
||||||
|
entries: Object.entries(result.result).map(([key, longurl]) => ({
|
||||||
|
key: key.slice(1),
|
||||||
|
shorturl: new URL(key, c.req.url),
|
||||||
|
longurl,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
{ status: 200 },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
async function createLink(c: Context) {
|
||||||
|
const url = c.req.headers.get('URL')
|
||||||
|
|
||||||
|
if (url == null || !validateUrl(url)) {
|
||||||
|
return c.json(
|
||||||
|
{
|
||||||
|
code: '400 Bad Request',
|
||||||
|
message: "No valid URL given. Please set a 'URL' header.",
|
||||||
|
},
|
||||||
|
400,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
await c.env.KV.put(c.get('key'), url)
|
||||||
|
return Response.json(
|
||||||
|
{
|
||||||
|
message: 'URL created succesfully.',
|
||||||
|
key: c.get('key').slice(1),
|
||||||
|
shorturl: c.get('shortUrl'),
|
||||||
|
longurl: url,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.post('/*', async (c) =>
|
||||||
|
c.json(
|
||||||
|
{
|
||||||
|
code: '405 Method Not Allowed',
|
||||||
|
message: 'POST not valid for individual keys. Did you mean PUT?',
|
||||||
|
},
|
||||||
|
405,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
app.all('*', (c) =>
|
||||||
|
c.json(
|
||||||
|
{
|
||||||
|
code: '405 Method Not Allowed',
|
||||||
|
message:
|
||||||
|
'Unsupported method. Please use one of GET, PUT, POST, DELETE, HEAD.',
|
||||||
|
},
|
||||||
|
405,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// PLAUSIBLE_HOST should be the full URL to your Plausible Analytics instance
|
||||||
|
// e.g. https://plausible.io/
|
||||||
|
async function sendToPlausible(c: Context) {
|
||||||
|
const url = c.env.PLAUSIBLE_HOST + 'api/event'
|
||||||
|
const headers = new Headers()
|
||||||
|
headers.append('User-Agent', c.req.headers.get('User-Agent') || '')
|
||||||
|
headers.append('X-Forwarded-For', c.req.headers.get('X-Forwarded-For') || '')
|
||||||
|
headers.append('Content-Type', 'application/json')
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
name: 'pageview',
|
||||||
|
url: c.req.url,
|
||||||
|
domain: new URL(c.req.url).hostname,
|
||||||
|
referrer: c.req.referrer,
|
||||||
|
}
|
||||||
|
await fetch(url, { method: 'POST', headers, body: JSON.stringify(data) })
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateUrl(url: string) {
|
||||||
|
// quick and dirty validation
|
||||||
|
if (url == '') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
new URL(url)
|
||||||
|
} catch (TypeError) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app
|
||||||
14
tsconfig.json
Normal file
14
tsconfig.json
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"strict": true,
|
||||||
|
"lib": ["esnext"],
|
||||||
|
"types": ["@cloudflare/workers-types"],
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
// "jsxFragmentFactory": "Fragment",
|
||||||
|
"jsxImportSource": "hono/jsx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,16 +1,15 @@
|
||||||
name = "worker-links"
|
name = "worker-links"
|
||||||
main = "index.js"
|
main = "src/index.ts"
|
||||||
no_bundle = true
|
|
||||||
|
|
||||||
# optional
|
# optional
|
||||||
#logpush = true
|
#logpush = true
|
||||||
|
|
||||||
# Change these!!
|
# Change these!!
|
||||||
account_id = "ece1d09b06af2ced51407c97505ea0cc"
|
account_id = "ece1d09b06af2ced51407c97505ea0cc"
|
||||||
kv_namespaces = [ { binding = "kv", id = "1be44406edc142a084435e24dbf8ae1d", preview_id = "15cfb90ecd654b8f8a9ccd600832093f" }]
|
kv_namespaces = [ { binding = "KV", id = "1be44406edc142a084435e24dbf8ae1d", preview_id = "15cfb90ecd654b8f8a9ccd600832093f" }]
|
||||||
|
|
||||||
compatibility_flags = []
|
compatibility_flags = []
|
||||||
compatibility_date = "2022-07-05"
|
compatibility_date = "2023-03-05"
|
||||||
|
|
||||||
# Remove or comment out the route line if using workers_dev
|
# Remove or comment out the route line if using workers_dev
|
||||||
workers_dev = false
|
workers_dev = false
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue