Skip to content

Commit d097a91

Browse files
Peejafforbeck
andauthored
refactor: Type check and restructure tests (#119)
This is more what I was getting at with https://github.com/storacha/freeway/pull/116/files#r1787752932. This avoids some of the stashing and mutation by putting more inside the `new Promise()` construction itself. I've also pulled `wranglerEnv` out into its own `const`, because it's actually an _input_, not an output. Then I've made `workerInfo` a `let` which is assigned when Wrangler boots, and is `undefined` before that, signaling that Wrangler is not yet available. The previous code was failing because there were `undefined` fields that aren't typed to allow `undefined`. Which wasn't an issue until… I also turned type checking on in `test/`, because apparently it wasn't already. 😛 And lastly, I've ignored `.wrangler`, because Wrangler writes to that when it runs. --------- Co-authored-by: Felipe Forbeck <[email protected]>
1 parent 9444729 commit d097a91

File tree

13 files changed

+503
-281
lines changed

13 files changed

+503
-281
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ node_modules
22
dist
33
.mf
44
.env
5-
.dev.vars
5+
.dev.vars
6+
.wrangler

package-lock.json

Lines changed: 115 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@
4444
},
4545
"devDependencies": {
4646
"@cloudflare/workers-types": "^4.20231218.0",
47+
"@types/chai": "^5.0.0",
48+
"@types/mocha": "^10.0.9",
49+
"@types/node-fetch": "^2.6.11",
50+
"@types/sinon": "^17.0.3",
4751
"@ucanto/principal": "^8.1.0",
4852
"@web3-storage/content-claims": "^5.0.0",
4953
"@web3-storage/public-bucket": "^1.1.0",
@@ -58,7 +62,7 @@
5862
"sinon": "^19.0.2",
5963
"standard": "^17.1.0",
6064
"tree-kill": "^1.2.2",
61-
"typescript": "^5.3.3",
65+
"typescript": "^5.6.3",
6266
"wrangler": "^3.78.8"
6367
},
6468
"standard": {

src/bindings.d.ts

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,10 @@
1-
import type { R2Bucket, KVNamespace, RateLimit } from '@cloudflare/workers-types'
21
import { CID } from '@web3-storage/gateway-lib/handlers'
3-
import { RATE_LIMIT_EXCEEDED } from './constants.js'
2+
import { Environment as RateLimiterEnvironment } from './handlers/rate-limiter.types.ts'
3+
import { Environment as CarBlockEnvironment } from './handlers/car-block.types.ts'
44

5-
export { }
6-
7-
export interface Environment {
5+
export interface Environment extends CarBlockEnvironment, RateLimiterEnvironment {
86
VERSION: string
9-
DEBUG: string
10-
CARPARK: R2Bucket
117
CONTENT_CLAIMS_SERVICE_URL?: string
12-
ACCOUNTING_SERVICE_URL: string
13-
RATE_LIMITER: RateLimit
14-
AUTH_TOKEN_METADATA: KVNamespace
15-
FF_RATE_LIMITER_ENABLED: string
16-
}
17-
18-
export type RateLimitExceeded = typeof RATE_LIMIT_EXCEEDED[keyof typeof RATE_LIMIT_EXCEEDED]
19-
20-
export interface RateLimitService {
21-
check: (cid: CID, req: Request) => Promise<RateLimitExceeded>
22-
}
23-
24-
export interface TokenMetadata {
25-
locationClaim?: unknown // TODO: figure out the right type to use for this - we probably need it for the private data case to verify auth
26-
invalid?: boolean
278
}
289

2910
export interface AccountingService {

src/handlers/car-block.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,17 @@ import { base58btc } from 'multiformats/bases/base58'
77
import { CAR_CODE } from '../constants.js'
88

99
/**
10-
* @typedef {import('@web3-storage/gateway-lib').IpfsUrlContext} CarBlockHandlerContext
11-
* @typedef {{ offset: number, length?: number } | { offset?: number, length: number } | { suffix: number }} Range
10+
* @import { Context, IpfsUrlContext as CarBlockHandlerContext, Handler } from '@web3-storage/gateway-lib'
11+
* @import { R2Bucket, KVNamespace, RateLimit } from '@cloudflare/workers-types'
12+
* @import { Environment } from './car-block.types.js'
1213
*/
1314

15+
/** @typedef {{ offset: number, length?: number } | { offset?: number, length: number } | { suffix: number }} Range */
16+
1417
/**
1518
* Handler that serves CAR files directly from R2.
1619
*
17-
* @type {import('@web3-storage/gateway-lib').Handler<CarBlockHandlerContext, import('../bindings.js').Environment>}
20+
* @type {Handler<CarBlockHandlerContext, Environment>}
1821
*/
1922
export async function handleCarBlock (request, env, ctx) {
2023
const { searchParams, dataCid } = ctx

src/handlers/car-block.types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { Environment as MiddlewareEnvironment } from '@web3-storage/gateway-lib'
2+
import { R2Bucket } from '@cloudflare/workers-types'
3+
4+
export interface Environment extends MiddlewareEnvironment {
5+
CARPARK: R2Bucket
6+
}

src/handlers/rate-limiter.js

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@ import { RATE_LIMIT_EXCEEDED } from '../constants.js'
33
import { Accounting } from '../services/accounting.js'
44

55
/**
6-
* @typedef {import('../bindings.js').Environment} Environment
7-
* @typedef {import('@web3-storage/gateway-lib').IpfsUrlContext} IpfsUrlContext
8-
* @typedef {import('../bindings.js').RateLimitService} RateLimitService
9-
* @typedef {import('../bindings.js').RateLimitExceeded} RateLimitExceeded
6+
* @import { Context, IpfsUrlContext, Middleware } from '@web3-storage/gateway-lib'
7+
* @import { R2Bucket, KVNamespace, RateLimit } from '@cloudflare/workers-types'
8+
* @import {
9+
* Environment,
10+
* TokenMetadata,
11+
* RateLimitService,
12+
* RateLimitExceeded
13+
* } from './rate-limiter.types.js'
1014
*/
1115

1216
/**
@@ -15,7 +19,7 @@ import { Accounting } from '../services/accounting.js'
1519
* it can be enabled or disabled using the FF_RATE_LIMITER_ENABLED flag.
1620
* Every successful request is recorded in the accounting service.
1721
*
18-
* @type {import('@web3-storage/gateway-lib').Middleware<IpfsUrlContext, IpfsUrlContext, Environment>}
22+
* @type {Middleware<IpfsUrlContext, IpfsUrlContext, Environment>}
1923
*/
2024
export function withRateLimit (handler) {
2125
return async (req, env, ctx) => {
@@ -29,7 +33,9 @@ export function withRateLimit (handler) {
2933
if (isRateLimitExceeded === RATE_LIMIT_EXCEEDED.YES) {
3034
throw new HttpError('Too Many Requests', { status: 429 })
3135
} else {
32-
const accounting = Accounting.create({ serviceURL: env.ACCOUNTING_SERVICE_URL })
36+
const accounting = Accounting.create({
37+
serviceURL: env.ACCOUNTING_SERVICE_URL
38+
})
3339
// NOTE: non-blocking call to the accounting service
3440
ctx.waitUntil(accounting.record(dataCid, req))
3541
return handler(req, env, ctx)
@@ -89,7 +95,7 @@ async function getAuthorizationTokenFromRequest (request) {
8995
}
9096

9197
/**
92-
* @param {import('@cloudflare/workers-types').RateLimit} rateLimitAPI
98+
* @param {RateLimit} rateLimitAPI
9399
* @param {import('multiformats/cid').CID} cid
94100
* @returns {Promise<import('../constants.js').RATE_LIMIT_EXCEEDED>}
95101
* @throws {Error} if no rate limit API is found
@@ -108,10 +114,10 @@ async function isRateLimited (rateLimitAPI, cid) {
108114
}
109115

110116
/**
111-
* @param {import("../bindings.js").Environment} env
117+
* @param {Environment} env
112118
* @param {string} authToken
113-
* @param {import('@web3-storage/gateway-lib').Context} ctx
114-
* @returns {Promise<import('../bindings.js').TokenMetadata | null>}
119+
* @param {Context} ctx
120+
* @returns {Promise<TokenMetadata | null>}
115121
*/
116122
async function getTokenMetadata (env, authToken, ctx) {
117123
const cachedValue = await env.AUTH_TOKEN_METADATA.get(authToken)
@@ -121,7 +127,9 @@ async function getTokenMetadata (env, authToken, ctx) {
121127
return decode(cachedValue)
122128
}
123129

124-
const accounting = Accounting.create({ serviceURL: env.ACCOUNTING_SERVICE_URL })
130+
const accounting = Accounting.create({
131+
serviceURL: env.ACCOUNTING_SERVICE_URL
132+
})
125133
const tokenMetadata = await accounting.getTokenMetadata(authToken)
126134
if (tokenMetadata) {
127135
// NOTE: non-blocking call to the auth token metadata cache
@@ -134,15 +142,15 @@ async function getTokenMetadata (env, authToken, ctx) {
134142

135143
/**
136144
* @param {string} s
137-
* @returns {import('../bindings.js').TokenMetadata}
145+
* @returns {TokenMetadata}
138146
*/
139147
function decode (s) {
140148
// TODO should this be dag-json?
141149
return JSON.parse(s)
142150
}
143151

144152
/**
145-
* @param {import('../bindings.js').TokenMetadata} m
153+
* @param {TokenMetadata} m
146154
* @returns {string}
147155
*/
148156
function encode (m) {

0 commit comments

Comments
 (0)