From e86dd554bee9987a1f4cf1f4077d7832abc67c3d Mon Sep 17 00:00:00 2001 From: Brandon Liu Date: Mon, 5 Feb 2024 16:59:06 +0800 Subject: [PATCH] lint cloudflare implementation (#352) Add linters to AWS and cloudflare implementations [#287] --- .github/workflows/actions.yml | 4 +- serverless/aws/package-lock.json | 228 ++++++++++++++++++++++++ serverless/aws/package.json | 5 +- serverless/aws/src/aws_region.test.ts | 24 +-- serverless/aws/src/aws_region.ts | 18 +- serverless/aws/src/index.ts | 93 +++++----- serverless/cloudflare/package-lock.json | 228 ++++++++++++++++++++++++ serverless/cloudflare/package.json | 5 +- serverless/cloudflare/src/index.ts | 119 +++++++------ 9 files changed, 598 insertions(+), 126 deletions(-) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index c5d6df6..14578ea 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -21,8 +21,8 @@ jobs: - run: cd js && npm install && npm run build - run: echo "VITE_GIT_SHA=$(git rev-parse --short HEAD)" >> app/.env - run: cd app && npm install && ./node_modules/.bin/tsc && npm run prettier-check && ./node_modules/.bin/vite build --base=/PMTiles/ - - run: cd serverless/aws && npm install && npx tsc && npm run build && cp dist/lambda_function.zip ../../app/dist - - run: cd serverless/cloudflare && cp wrangler.toml.example wrangler.toml && npm install && npx tsc && npm run build && cp dist/index.js ../../app/dist + - run: cd serverless/aws && npm install && npx tsc && npm run biome-check && npm run build && cp dist/lambda_function.zip ../../app/dist + - run: cd serverless/cloudflare && cp wrangler.toml.example wrangler.toml && npm install && npx tsc && npm run biome-check && npm run build && cp dist/index.js ../../app/dist - run: cd spec/v3 && cp *.pmtiles ../../app/dist - run: cd js/examples && mkdir ../../app/dist/examples && cp *.html ../../app/dist/examples/ - run: cd openlayers/examples && mkdir ../../app/dist/examples/openlayers && cp *.html ../../app/dist/examples/openlayers diff --git a/serverless/aws/package-lock.json b/serverless/aws/package-lock.json index fa31ee4..b7b8af3 100644 --- a/serverless/aws/package-lock.json +++ b/serverless/aws/package-lock.json @@ -12,6 +12,7 @@ "@aws-sdk/node-http-handler": "^3.360.0" }, "devDependencies": { + "@biomejs/biome": "^1.5.3", "@types/aws-lambda": "^8.10.108", "@types/node": "^18.11.2", "esbuild": "^0.20.0", @@ -915,6 +916,161 @@ "node": ">=14.0.0" } }, + "node_modules/@biomejs/biome": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.5.3.tgz", + "integrity": "sha512-yvZCa/g3akwTaAQ7PCwPWDCkZs3Qa5ONg/fgOUT9e6wAWsPftCjLQFPXBeGxPK30yZSSpgEmRCfpGTmVbUjGgg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "1.5.3", + "@biomejs/cli-darwin-x64": "1.5.3", + "@biomejs/cli-linux-arm64": "1.5.3", + "@biomejs/cli-linux-arm64-musl": "1.5.3", + "@biomejs/cli-linux-x64": "1.5.3", + "@biomejs/cli-linux-x64-musl": "1.5.3", + "@biomejs/cli-win32-arm64": "1.5.3", + "@biomejs/cli-win32-x64": "1.5.3" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.5.3.tgz", + "integrity": "sha512-ImU7mh1HghEDyqNmxEZBoMPr8SxekkZuYcs+gynKlNW+TALQs7swkERiBLkG9NR0K1B3/2uVzlvYowXrmlW8hw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.5.3.tgz", + "integrity": "sha512-vCdASqYnlpq/swErH7FD6nrFz0czFtK4k/iLgj0/+VmZVjineFPgevOb+Sr9vz0tk0GfdQO60bSpI74zU8M9Dw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.5.3.tgz", + "integrity": "sha512-cupBQv0sNF1OKqBfx7EDWMSsKwRrBUZfjXawT4s6hKV6ALq7p0QzWlxr/sDmbKMLOaLQtw2Qgu/77N9rm+f9Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.5.3.tgz", + "integrity": "sha512-DYuMizUYUBYfS0IHGjDrOP1RGipqWfMGEvNEJ398zdtmCKLXaUvTimiox5dvx4X15mBK5M2m8wgWUgOP1giUpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.5.3.tgz", + "integrity": "sha512-YQrSArQvcv4FYsk7Q91Yv4uuu5F8hJyORVcv3zsjCLGkjIjx2RhjYLpTL733SNL7v33GmOlZY0eFR1ko38tuUw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.5.3.tgz", + "integrity": "sha512-UUHiAnlDqr2Y/LpvshBFhUYMWkl2/Jn+bi3U6jKuav0qWbbBKU/ByHgR4+NBxpKBYoCtWxhnmatfH1bpPIuZMw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.5.3.tgz", + "integrity": "sha512-HxatYH7vf/kX9nrD+pDYuV2GI9GV8EFo6cfKkahAecTuZLPxryHx1WEfJthp5eNsE0+09STGkKIKjirP0ufaZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.5.3.tgz", + "integrity": "sha512-fMvbSouZEASU7mZH8SIJSANDm5OqsjgtVXlbUqxwed6BP7uuHRSs396Aqwh2+VoW8fwTpp6ybIUoC9FrzB0kyA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.*" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.20.0", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.0.tgz", @@ -3168,6 +3324,78 @@ "tslib": "^2.5.0" } }, + "@biomejs/biome": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.5.3.tgz", + "integrity": "sha512-yvZCa/g3akwTaAQ7PCwPWDCkZs3Qa5ONg/fgOUT9e6wAWsPftCjLQFPXBeGxPK30yZSSpgEmRCfpGTmVbUjGgg==", + "dev": true, + "requires": { + "@biomejs/cli-darwin-arm64": "1.5.3", + "@biomejs/cli-darwin-x64": "1.5.3", + "@biomejs/cli-linux-arm64": "1.5.3", + "@biomejs/cli-linux-arm64-musl": "1.5.3", + "@biomejs/cli-linux-x64": "1.5.3", + "@biomejs/cli-linux-x64-musl": "1.5.3", + "@biomejs/cli-win32-arm64": "1.5.3", + "@biomejs/cli-win32-x64": "1.5.3" + } + }, + "@biomejs/cli-darwin-arm64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.5.3.tgz", + "integrity": "sha512-ImU7mh1HghEDyqNmxEZBoMPr8SxekkZuYcs+gynKlNW+TALQs7swkERiBLkG9NR0K1B3/2uVzlvYowXrmlW8hw==", + "dev": true, + "optional": true + }, + "@biomejs/cli-darwin-x64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.5.3.tgz", + "integrity": "sha512-vCdASqYnlpq/swErH7FD6nrFz0czFtK4k/iLgj0/+VmZVjineFPgevOb+Sr9vz0tk0GfdQO60bSpI74zU8M9Dw==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-arm64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.5.3.tgz", + "integrity": "sha512-cupBQv0sNF1OKqBfx7EDWMSsKwRrBUZfjXawT4s6hKV6ALq7p0QzWlxr/sDmbKMLOaLQtw2Qgu/77N9rm+f9Rg==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-arm64-musl": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.5.3.tgz", + "integrity": "sha512-DYuMizUYUBYfS0IHGjDrOP1RGipqWfMGEvNEJ398zdtmCKLXaUvTimiox5dvx4X15mBK5M2m8wgWUgOP1giUpQ==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-x64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.5.3.tgz", + "integrity": "sha512-YQrSArQvcv4FYsk7Q91Yv4uuu5F8hJyORVcv3zsjCLGkjIjx2RhjYLpTL733SNL7v33GmOlZY0eFR1ko38tuUw==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-x64-musl": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.5.3.tgz", + "integrity": "sha512-UUHiAnlDqr2Y/LpvshBFhUYMWkl2/Jn+bi3U6jKuav0qWbbBKU/ByHgR4+NBxpKBYoCtWxhnmatfH1bpPIuZMw==", + "dev": true, + "optional": true + }, + "@biomejs/cli-win32-arm64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.5.3.tgz", + "integrity": "sha512-HxatYH7vf/kX9nrD+pDYuV2GI9GV8EFo6cfKkahAecTuZLPxryHx1WEfJthp5eNsE0+09STGkKIKjirP0ufaZA==", + "dev": true, + "optional": true + }, + "@biomejs/cli-win32-x64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.5.3.tgz", + "integrity": "sha512-fMvbSouZEASU7mZH8SIJSANDm5OqsjgtVXlbUqxwed6BP7uuHRSs396Aqwh2+VoW8fwTpp6ybIUoC9FrzB0kyA==", + "dev": true, + "optional": true + }, "@esbuild/aix-ppc64": { "version": "0.20.0", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.0.tgz", diff --git a/serverless/aws/package.json b/serverless/aws/package.json index 3d6662a..2d6e3a0 100644 --- a/serverless/aws/package.json +++ b/serverless/aws/package.json @@ -2,6 +2,7 @@ "name": "pmtiles-aws", "version": "0.0.0", "devDependencies": { + "@biomejs/biome": "^1.5.3", "@types/aws-lambda": "^8.10.108", "@types/node": "^18.11.2", "esbuild": "^0.20.0", @@ -12,7 +13,9 @@ "scripts": { "tsc": "tsc --noEmit --watch", "build": "esbuild src/index.ts --target=es2020 --outfile=dist/index.mjs --format=esm --bundle --platform=node --target=node18 --external:@aws-sdk/client-s3 --external:@aws-sdk/node-http-handler --banner:js=//$(git describe --always) && cd dist && zip lambda_function.zip index.mjs", - "test": "tsx ../shared/index.test.ts" + "test": "tsx ../shared/index.test.ts", + "biome": "biome check --config-path=../../js/ src --apply", + "biome-check": "biome check --config-path=../../js src" }, "dependencies": { "@aws-sdk/client-s3": "^3.360.0", diff --git a/serverless/aws/src/aws_region.test.ts b/serverless/aws/src/aws_region.test.ts index d7d0fb5..c917b82 100644 --- a/serverless/aws/src/aws_region.test.ts +++ b/serverless/aws/src/aws_region.test.ts @@ -1,37 +1,37 @@ -import { test } from "node:test"; import assert from "node:assert"; +import { test } from "node:test"; -import { get_region } from "./aws_region"; +import { getRegion } from "./aws_region"; test("one bucket", () => { - let result = get_region( + const result = getRegion( "us-west-1", { bucket: "mybucket", region: "us-west-1" }, - [], + [] ); assert.deepEqual(result, { bucket: "mybucket", region: "us-west-1" }); }); test("unknown region", () => { - let result = get_region( + const result = getRegion( "us-nullisland-1", { bucket: "mybucket", region: "us-west-1" }, - [], + [] ); assert.deepEqual(result, { bucket: "mybucket", region: "us-west-1" }); }); test("exact region match", () => { - let result = get_region( + let result = getRegion( "us-west-1", { bucket: "mybucket", region: "us-west-1" }, - [{ bucket: "mybucket-ap-south-1", region: "ap-south-1" }], + [{ bucket: "mybucket-ap-south-1", region: "ap-south-1" }] ); assert.deepEqual(result, { bucket: "mybucket", region: "us-west-1" }); - result = get_region( + result = getRegion( "ap-south-1", { bucket: "mybucket", region: "us-west-1" }, - [{ bucket: "mybucket-ap-south-1", region: "ap-south-1" }], + [{ bucket: "mybucket-ap-south-1", region: "ap-south-1" }] ); assert.deepEqual(result, { bucket: "mybucket-ap-south-1", @@ -40,10 +40,10 @@ test("exact region match", () => { }); test("priority match", () => { - let result = get_region( + const result = getRegion( "us-west-1", { bucket: "mybucket", region: "ap-south-1" }, - [{ bucket: "mybucket-us-west-2", region: "us-west-2" }], + [{ bucket: "mybucket-us-west-2", region: "us-west-2" }] ); assert.deepEqual(result, { bucket: "mybucket-us-west-2", diff --git a/serverless/aws/src/aws_region.ts b/serverless/aws/src/aws_region.ts index 29036d3..85f6225 100644 --- a/serverless/aws/src/aws_region.ts +++ b/serverless/aws/src/aws_region.ts @@ -19,24 +19,24 @@ const REGION_MATRIX: Record = { "sa-east-1": ["us-east-1", "us-east-2"], // sao paulo }; -export let get_region = ( - exec_region: string, +export const getRegion = ( + execRegion: string, primary: Bucket, - replicas: Bucket[], + replicas: Bucket[] ): Bucket => { - if (primary.region === exec_region) { + if (primary.region === execRegion) { return primary; } - for (let replica of replicas) { - if (replica.region === exec_region) { + for (const replica of replicas) { + if (replica.region === execRegion) { return replica; } } - if (exec_region in REGION_MATRIX) { - for (let region of REGION_MATRIX[exec_region]) { - for (let replica of replicas) { + if (execRegion in REGION_MATRIX) { + for (const region of REGION_MATRIX[execRegion]) { + for (const replica of replicas) { if (replica.region === region) { return replica; } diff --git a/serverless/aws/src/index.ts b/serverless/aws/src/index.ts index 4b5af2e..ea62071 100644 --- a/serverless/aws/src/index.ts +++ b/serverless/aws/src/index.ts @@ -1,22 +1,22 @@ import { - Context, - APIGatewayProxyResult, APIGatewayProxyEventV2, + APIGatewayProxyResult, + Context, } from "aws-lambda"; import { - PMTiles, - ResolvedValueCache, - RangeResponse, - Source, Compression, + PMTiles, + RangeResponse, + ResolvedValueCache, + Source, TileType, } from "../../../js/index"; -import { pmtiles_path, tile_path, tileJSON } from "../../shared/index"; +import { pmtiles_path, tileJSON, tile_path } from "../../shared/index"; +import { createHash } from "crypto"; import zlib from "zlib"; -import { createHash } from "crypto" -import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3"; +import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3"; import { NodeHttpHandler } from "@aws-sdk/node-http-handler"; // the region should default to the same one as the function @@ -33,37 +33,47 @@ async function nativeDecompress( ): Promise { if (compression === Compression.None || compression === Compression.Unknown) { return buf; - } else if (compression === Compression.Gzip) { - return zlib.gunzipSync(buf); - } else { - throw Error("Compression method not supported"); } + if (compression === Compression.Gzip) { + return zlib.gunzipSync(buf); + } + throw Error("Compression method not supported"); } // Lambda needs to run with 512MB, empty function takes about 70 const CACHE = new ResolvedValueCache(undefined, undefined, nativeDecompress); class S3Source implements Source { - archive_name: string; + archiveName: string; - constructor(archive_name: string) { - this.archive_name = archive_name; + constructor(archiveName: string) { + this.archiveName = archiveName; } getKey() { - return this.archive_name; + return this.archiveName; } - async getBytes(offset: number, length: number, signal?:AbortSignal, etag?: string): Promise { + async getBytes( + offset: number, + length: number, + signal?: AbortSignal, + etag?: string + ): Promise { const resp = await s3client.send( new GetObjectCommand({ + // biome-ignore lint: aws api Bucket: process.env.BUCKET!, - Key: pmtiles_path(this.archive_name, process.env.PMTILES_PATH), + // biome-ignore lint: aws api + Key: pmtiles_path(this.archiveName, process.env.PMTILES_PATH), + // biome-ignore lint: aws api Range: "bytes=" + offset + "-" + (offset + length - 1), }) ); - const arr = await resp.Body!.transformToByteArray(); + const arr = await resp.Body?.transformToByteArray(); + + if (!arr) throw Error("Failed to read S3 response body"); return { data: arr.buffer, @@ -100,12 +110,12 @@ export const handlerRaw = async ( _context: Context, tilePostprocess?: (a: ArrayBuffer, t: TileType) => ArrayBuffer ): Promise => { - let path; - let is_api_gateway; + let path: string; + let isApiGateway = false; if (event.pathParameters) { - is_api_gateway = true; + isApiGateway = true; if (event.pathParameters.proxy) { - path = "/" + event.pathParameters.proxy; + path = `/${event.pathParameters.proxy}`; } else { return apiResp(500, "Proxy integration missing tile_path parameter"); } @@ -167,7 +177,7 @@ export const handlerRaw = async ( [TileType.Avif, "avif"], ]) { if (header.tileType === pair[0] && ext !== pair[1]) { - if (header.tileType == TileType.Mvt && ext === "pbf") { + if (header.tileType === TileType.Mvt && ext === "pbf") { // allow this for now. Eventually we will delete this in favor of .mvt continue; } @@ -180,8 +190,8 @@ export const handlerRaw = async ( } } - const tile_result = await p.getZxy(tile[0], tile[1], tile[2]); - if (tile_result) { + const tileResult = await p.getZxy(tile[0], tile[1], tile[2]); + if (tileResult) { switch (header.tileType) { case TileType.Mvt: // part of the list of Cloudfront compressible types. @@ -201,38 +211,35 @@ export const handlerRaw = async ( break; } - let data = tile_result.data; + let data = tileResult.data; if (tilePostprocess) { data = tilePostprocess(data, header.tileType); } - headers["Cache-Control"] = `public, max-age=${process.env.CACHE_MAX_AGE || 86400}`; - headers["ETag"] = `"${createHash("sha256").update(Buffer.from(data)).digest("hex")}"` + headers["Cache-Control"] = `public, max-age=${ + process.env.CACHE_MAX_AGE || 86400 + }`; + headers.ETag = `"${createHash("sha256") + .update(Buffer.from(data)) + .digest("hex")}"`; - if (is_api_gateway) { + if (isApiGateway) { // this is wasted work, but we need to force API Gateway to interpret the Lambda response as binary // without depending on clients sending matching Accept: headers in the request. - const recompressed_data = zlib.gzipSync(data); + const recompressedData = zlib.gzipSync(data); headers["Content-Encoding"] = "gzip"; return apiResp( 200, - Buffer.from(recompressed_data).toString("base64"), - true, - headers - ); - } else { - // returns uncompressed response - return apiResp( - 200, - Buffer.from(data).toString("base64"), + Buffer.from(recompressedData).toString("base64"), true, headers ); } - } else { - return apiResp(204, "", false, headers); + // returns uncompressed response + return apiResp(200, Buffer.from(data).toString("base64"), true, headers); } + return apiResp(204, "", false, headers); } catch (e) { if ((e as Error).name === "AccessDenied") { return apiResp(403, "Bucket access unauthorized", false, headers); diff --git a/serverless/cloudflare/package-lock.json b/serverless/cloudflare/package-lock.json index a257208..6170c5e 100644 --- a/serverless/cloudflare/package-lock.json +++ b/serverless/cloudflare/package-lock.json @@ -8,12 +8,168 @@ "name": "pmtiles-cloudflare", "version": "0.0.1", "devDependencies": { + "@biomejs/biome": "^1.5.3", "@cloudflare/workers-types": "^4.20230518.0", "tsx": "^4.7.0", "typescript": "^4.8.4", "wrangler": "3.19.0" } }, + "node_modules/@biomejs/biome": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.5.3.tgz", + "integrity": "sha512-yvZCa/g3akwTaAQ7PCwPWDCkZs3Qa5ONg/fgOUT9e6wAWsPftCjLQFPXBeGxPK30yZSSpgEmRCfpGTmVbUjGgg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "1.5.3", + "@biomejs/cli-darwin-x64": "1.5.3", + "@biomejs/cli-linux-arm64": "1.5.3", + "@biomejs/cli-linux-arm64-musl": "1.5.3", + "@biomejs/cli-linux-x64": "1.5.3", + "@biomejs/cli-linux-x64-musl": "1.5.3", + "@biomejs/cli-win32-arm64": "1.5.3", + "@biomejs/cli-win32-x64": "1.5.3" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.5.3.tgz", + "integrity": "sha512-ImU7mh1HghEDyqNmxEZBoMPr8SxekkZuYcs+gynKlNW+TALQs7swkERiBLkG9NR0K1B3/2uVzlvYowXrmlW8hw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.5.3.tgz", + "integrity": "sha512-vCdASqYnlpq/swErH7FD6nrFz0czFtK4k/iLgj0/+VmZVjineFPgevOb+Sr9vz0tk0GfdQO60bSpI74zU8M9Dw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.5.3.tgz", + "integrity": "sha512-cupBQv0sNF1OKqBfx7EDWMSsKwRrBUZfjXawT4s6hKV6ALq7p0QzWlxr/sDmbKMLOaLQtw2Qgu/77N9rm+f9Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.5.3.tgz", + "integrity": "sha512-DYuMizUYUBYfS0IHGjDrOP1RGipqWfMGEvNEJ398zdtmCKLXaUvTimiox5dvx4X15mBK5M2m8wgWUgOP1giUpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.5.3.tgz", + "integrity": "sha512-YQrSArQvcv4FYsk7Q91Yv4uuu5F8hJyORVcv3zsjCLGkjIjx2RhjYLpTL733SNL7v33GmOlZY0eFR1ko38tuUw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.5.3.tgz", + "integrity": "sha512-UUHiAnlDqr2Y/LpvshBFhUYMWkl2/Jn+bi3U6jKuav0qWbbBKU/ByHgR4+NBxpKBYoCtWxhnmatfH1bpPIuZMw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.5.3.tgz", + "integrity": "sha512-HxatYH7vf/kX9nrD+pDYuV2GI9GV8EFo6cfKkahAecTuZLPxryHx1WEfJthp5eNsE0+09STGkKIKjirP0ufaZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.*" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.5.3.tgz", + "integrity": "sha512-fMvbSouZEASU7mZH8SIJSANDm5OqsjgtVXlbUqxwed6BP7uuHRSs396Aqwh2+VoW8fwTpp6ybIUoC9FrzB0kyA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.*" + } + }, "node_modules/@cloudflare/kv-asset-handler": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.2.0.tgz", @@ -1615,6 +1771,78 @@ } }, "dependencies": { + "@biomejs/biome": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.5.3.tgz", + "integrity": "sha512-yvZCa/g3akwTaAQ7PCwPWDCkZs3Qa5ONg/fgOUT9e6wAWsPftCjLQFPXBeGxPK30yZSSpgEmRCfpGTmVbUjGgg==", + "dev": true, + "requires": { + "@biomejs/cli-darwin-arm64": "1.5.3", + "@biomejs/cli-darwin-x64": "1.5.3", + "@biomejs/cli-linux-arm64": "1.5.3", + "@biomejs/cli-linux-arm64-musl": "1.5.3", + "@biomejs/cli-linux-x64": "1.5.3", + "@biomejs/cli-linux-x64-musl": "1.5.3", + "@biomejs/cli-win32-arm64": "1.5.3", + "@biomejs/cli-win32-x64": "1.5.3" + } + }, + "@biomejs/cli-darwin-arm64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.5.3.tgz", + "integrity": "sha512-ImU7mh1HghEDyqNmxEZBoMPr8SxekkZuYcs+gynKlNW+TALQs7swkERiBLkG9NR0K1B3/2uVzlvYowXrmlW8hw==", + "dev": true, + "optional": true + }, + "@biomejs/cli-darwin-x64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.5.3.tgz", + "integrity": "sha512-vCdASqYnlpq/swErH7FD6nrFz0czFtK4k/iLgj0/+VmZVjineFPgevOb+Sr9vz0tk0GfdQO60bSpI74zU8M9Dw==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-arm64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.5.3.tgz", + "integrity": "sha512-cupBQv0sNF1OKqBfx7EDWMSsKwRrBUZfjXawT4s6hKV6ALq7p0QzWlxr/sDmbKMLOaLQtw2Qgu/77N9rm+f9Rg==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-arm64-musl": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.5.3.tgz", + "integrity": "sha512-DYuMizUYUBYfS0IHGjDrOP1RGipqWfMGEvNEJ398zdtmCKLXaUvTimiox5dvx4X15mBK5M2m8wgWUgOP1giUpQ==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-x64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.5.3.tgz", + "integrity": "sha512-YQrSArQvcv4FYsk7Q91Yv4uuu5F8hJyORVcv3zsjCLGkjIjx2RhjYLpTL733SNL7v33GmOlZY0eFR1ko38tuUw==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-x64-musl": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.5.3.tgz", + "integrity": "sha512-UUHiAnlDqr2Y/LpvshBFhUYMWkl2/Jn+bi3U6jKuav0qWbbBKU/ByHgR4+NBxpKBYoCtWxhnmatfH1bpPIuZMw==", + "dev": true, + "optional": true + }, + "@biomejs/cli-win32-arm64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.5.3.tgz", + "integrity": "sha512-HxatYH7vf/kX9nrD+pDYuV2GI9GV8EFo6cfKkahAecTuZLPxryHx1WEfJthp5eNsE0+09STGkKIKjirP0ufaZA==", + "dev": true, + "optional": true + }, + "@biomejs/cli-win32-x64": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.5.3.tgz", + "integrity": "sha512-fMvbSouZEASU7mZH8SIJSANDm5OqsjgtVXlbUqxwed6BP7uuHRSs396Aqwh2+VoW8fwTpp6ybIUoC9FrzB0kyA==", + "dev": true, + "optional": true + }, "@cloudflare/kv-asset-handler": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.2.0.tgz", diff --git a/serverless/cloudflare/package.json b/serverless/cloudflare/package.json index 638b09f..f093a02 100644 --- a/serverless/cloudflare/package.json +++ b/serverless/cloudflare/package.json @@ -2,6 +2,7 @@ "name": "pmtiles-cloudflare", "version": "0.0.1", "devDependencies": { + "@biomejs/biome": "^1.5.3", "@cloudflare/workers-types": "^4.20230518.0", "tsx": "^4.7.0", "typescript": "^4.8.4", @@ -13,6 +14,8 @@ "deploy": "wrangler deploy", "test": "tsx ../shared/index.test.ts", "tsc": "tsc --watch", - "build": "wrangler publish --outdir dist --dry-run" + "build": "wrangler publish --outdir dist --dry-run", + "biome": "biome check --config-path=../../js/ src/index.ts --apply", + "biome-check": "biome check --config-path=../../js src/index.ts" } } diff --git a/serverless/cloudflare/src/index.ts b/serverless/cloudflare/src/index.ts index a591e29..55219c9 100644 --- a/serverless/cloudflare/src/index.ts +++ b/serverless/cloudflare/src/index.ts @@ -1,26 +1,27 @@ import { + Compression, PMTiles, - Source, RangeResponse, ResolvedValueCache, + Source, TileType, - Compression, } from "../../../js/index"; -import { pmtiles_path, tile_path, tileJSON } from "../../shared/index"; +import { pmtiles_path, tileJSON, tile_path } from "../../shared/index"; interface Env { + // biome-ignore lint: config name ALLOWED_ORIGINS?: string; + // biome-ignore lint: config name BUCKET: R2Bucket; + // biome-ignore lint: config name CACHE_MAX_AGE?: number; + // biome-ignore lint: config name PMTILES_PATH?: string; + // biome-ignore lint: config name PUBLIC_HOSTNAME?: string; } -class KeyNotFoundError extends Error { - constructor(message: string) { - super(message); - } -} +class KeyNotFoundError extends Error {} async function nativeDecompress( buf: ArrayBuffer, @@ -28,33 +29,38 @@ async function nativeDecompress( ): Promise { if (compression === Compression.None || compression === Compression.Unknown) { return buf; - } else if (compression === Compression.Gzip) { - let stream = new Response(buf).body!; - let result = stream.pipeThrough(new DecompressionStream("gzip")); - return new Response(result).arrayBuffer(); - } else { - throw Error("Compression method not supported"); } + if (compression === Compression.Gzip) { + const stream = new Response(buf).body; + const result = stream?.pipeThrough(new DecompressionStream("gzip")); + return new Response(result).arrayBuffer(); + } + throw Error("Compression method not supported"); } const CACHE = new ResolvedValueCache(25, undefined, nativeDecompress); class R2Source implements Source { env: Env; - archive_name: string; + archiveName: string; - constructor(env: Env, archive_name: string) { + constructor(env: Env, archiveName: string) { this.env = env; - this.archive_name = archive_name; + this.archiveName = archiveName; } getKey() { - return this.archive_name; + return this.archiveName; } - async getBytes(offset: number, length: number, signal?: AbortSignal, etag?: string): Promise { + async getBytes( + offset: number, + length: number, + signal?: AbortSignal, + etag?: string + ): Promise { const resp = await this.env.BUCKET.get( - pmtiles_path(this.archive_name, this.env.PMTILES_PATH), + pmtiles_path(this.archiveName, this.env.PMTILES_PATH), { range: { offset: offset, length: length }, } @@ -88,73 +94,72 @@ export default { const cache = caches.default; if (ok) { - let allowed_origin = ""; + let allowedOrigin = ""; if (typeof env.ALLOWED_ORIGINS !== "undefined") { for (const o of env.ALLOWED_ORIGINS.split(",")) { if (o === request.headers.get("Origin") || o === "*") { - allowed_origin = o; + allowedOrigin = o; } } } const cached = await cache.match(request.url); if (cached) { - const resp_headers = new Headers(cached.headers); - if (allowed_origin) - resp_headers.set("Access-Control-Allow-Origin", allowed_origin); - resp_headers.set("Vary", "Origin"); + const respHeaders = new Headers(cached.headers); + if (allowedOrigin) + respHeaders.set("Access-Control-Allow-Origin", allowedOrigin); + respHeaders.set("Vary", "Origin"); return new Response(cached.body, { - headers: resp_headers, + headers: respHeaders, status: cached.status, }); } const cacheableResponse = ( body: ArrayBuffer | string | undefined, - cacheable_headers: Headers, + cacheableHeaders: Headers, status: number ) => { - cacheable_headers.set( + cacheableHeaders.set( "Cache-Control", - "max-age=" + (env.CACHE_MAX_AGE || 86400) + `max-age=${env.CACHE_MAX_AGE || 86400}` ); const cacheable = new Response(body, { - headers: cacheable_headers, + headers: cacheableHeaders, status: status, }); - // normalize HEAD requests ctx.waitUntil(cache.put(request.url, cacheable)); - const resp_headers = new Headers(cacheable_headers); - if (allowed_origin) - resp_headers.set("Access-Control-Allow-Origin", allowed_origin); - resp_headers.set("Vary", "Origin"); - return new Response(body, { headers: resp_headers, status: status }); + const respHeaders = new Headers(cacheableHeaders); + if (allowedOrigin) + respHeaders.set("Access-Control-Allow-Origin", allowedOrigin); + respHeaders.set("Vary", "Origin"); + return new Response(body, { headers: respHeaders, status: status }); }; - const cacheable_headers = new Headers(); + const cacheableHeaders = new Headers(); const source = new R2Source(env, name); const p = new PMTiles(source, CACHE, nativeDecompress); try { - const p_header = await p.getHeader(); + const pHeader = await p.getHeader(); if (!tile) { - cacheable_headers.set("Content-Type", "application/json"); + cacheableHeaders.set("Content-Type", "application/json"); const t = tileJSON( - p_header, + pHeader, await p.getMetadata(), env.PUBLIC_HOSTNAME || url.hostname, name ); - return cacheableResponse(JSON.stringify(t), cacheable_headers, 200); + return cacheableResponse(JSON.stringify(t), cacheableHeaders, 200); } - if (tile[0] < p_header.minZoom || tile[0] > p_header.maxZoom) { - return cacheableResponse(undefined, cacheable_headers, 404); + if (tile[0] < pHeader.minZoom || tile[0] > pHeader.maxZoom) { + return cacheableResponse(undefined, cacheableHeaders, 404); } for (const pair of [ @@ -164,14 +169,14 @@ export default { [TileType.Webp, "webp"], [TileType.Avif, "avif"], ]) { - if (p_header.tileType === pair[0] && ext !== pair[1]) { - if (p_header.tileType == TileType.Mvt && ext === "pbf") { + if (pHeader.tileType === pair[0] && ext !== pair[1]) { + if (pHeader.tileType === TileType.Mvt && ext === "pbf") { // allow this for now. Eventually we will delete this in favor of .mvt continue; } return cacheableResponse( `Bad request: requested .${ext} but archive has type .${pair[1]}`, - cacheable_headers, + cacheableHeaders, 400 ); } @@ -179,32 +184,30 @@ export default { const tiledata = await p.getZxy(tile[0], tile[1], tile[2]); - switch (p_header.tileType) { + switch (pHeader.tileType) { case TileType.Mvt: - cacheable_headers.set("Content-Type", "application/x-protobuf"); + cacheableHeaders.set("Content-Type", "application/x-protobuf"); break; case TileType.Png: - cacheable_headers.set("Content-Type", "image/png"); + cacheableHeaders.set("Content-Type", "image/png"); break; case TileType.Jpeg: - cacheable_headers.set("Content-Type", "image/jpeg"); + cacheableHeaders.set("Content-Type", "image/jpeg"); break; case TileType.Webp: - cacheable_headers.set("Content-Type", "image/webp"); + cacheableHeaders.set("Content-Type", "image/webp"); break; } if (tiledata) { - return cacheableResponse(tiledata.data, cacheable_headers, 200); - } else { - return cacheableResponse(undefined, cacheable_headers, 204); + return cacheableResponse(tiledata.data, cacheableHeaders, 200); } + return cacheableResponse(undefined, cacheableHeaders, 204); } catch (e) { if (e instanceof KeyNotFoundError) { - return cacheableResponse("Archive not found", cacheable_headers, 404); - } else { - throw e; + return cacheableResponse("Archive not found", cacheableHeaders, 404); } + throw e; } }