@@ -4,6 +4,7 @@ import initWasm, {
4
4
is_valid_email ,
5
5
type EmailValidationConfig ,
6
6
} from "./wasm/arcjet_analyze_js_req.js" ;
7
+ import { instantiate } from "./wasm/rate-limit/arcjet_analyze_bindings_rate_limit.component.js" ;
7
8
8
9
export { type EmailValidationConfig } ;
9
10
@@ -163,3 +164,114 @@ export async function detectBot(
163
164
} ;
164
165
}
165
166
}
167
+
168
+ function nowInSeconds ( ) : number {
169
+ return Math . floor ( Date . now ( ) / 1000 ) ;
170
+ }
171
+
172
+ class Cache < T > {
173
+ expires : Map < string , number > ;
174
+ data : Map < string , T > ;
175
+
176
+ constructor ( ) {
177
+ this . expires = new Map ( ) ;
178
+ this . data = new Map ( ) ;
179
+ }
180
+
181
+ get ( key : string ) {
182
+ const ttl = this . ttl ( key ) ;
183
+ if ( ttl > 0 ) {
184
+ return this . data . get ( key ) ;
185
+ } else {
186
+ // Cleanup if expired
187
+ this . expires . delete ( key ) ;
188
+ this . data . delete ( key ) ;
189
+ }
190
+ }
191
+
192
+ set ( key : string , value : T , ttl : number ) {
193
+ this . expires . set ( key , nowInSeconds ( ) + ttl ) ;
194
+ this . data . set ( key , value ) ;
195
+ }
196
+
197
+ ttl ( key : string ) : number {
198
+ const now = nowInSeconds ( ) ;
199
+ const expiresAt = this . expires . get ( key ) ?? now ;
200
+ return expiresAt - now ;
201
+ }
202
+ }
203
+
204
+ const rateLimitCache = new Cache < string > ( ) ;
205
+
206
+ export async function fixedWindow (
207
+ config : {
208
+ key : string ;
209
+ characteristics ?: string [ ] ;
210
+ max : number ;
211
+ window : number ;
212
+ } ,
213
+ request : unknown ,
214
+ ) : Promise < {
215
+ allowed : boolean ;
216
+ max : number ;
217
+ remaining : number ;
218
+ reset : number ;
219
+ } > {
220
+ const configJson = JSON . stringify ( config ) ;
221
+ const requestJson = JSON . stringify ( request ) ;
222
+
223
+ const abc = await instantiate (
224
+ async function ( path : string ) {
225
+ if ( process . env [ "NEXT_RUNTIME" ] === "edge" ) {
226
+ if ( path === "arcjet_analyze_bindings_rate_limit.component.core.wasm" ) {
227
+ const mod = await import (
228
+ // @ts -expect-error
229
+ "./wasm/rate-limit/arcjet_analyze_bindings_rate_limit.component.core.wasm?module"
230
+ ) ;
231
+ return mod . default ;
232
+ }
233
+ if (
234
+ path === "arcjet_analyze_bindings_rate_limit.component.core2.wasm"
235
+ ) {
236
+ const mod = await import (
237
+ // @ts -expect-error
238
+ "./wasm/rate-limit/arcjet_analyze_bindings_rate_limit.component.core2.wasm?module"
239
+ ) ;
240
+ return mod . default ;
241
+ }
242
+ if (
243
+ path === "arcjet_analyze_bindings_rate_limit.component.core3.wasm"
244
+ ) {
245
+ const mod = await import (
246
+ // @ts -expect-error
247
+ "./wasm/rate-limit/arcjet_analyze_bindings_rate_limit.component.core3.wasm?module"
248
+ ) ;
249
+ return mod . default ;
250
+ }
251
+ } else {
252
+ // const { wasm } = await import("./wasm/arcjet.wasm.js");
253
+ // wasmModule = await WebAssembly.compile(await wasm());
254
+ return Promise . reject ( "TODO" ) ;
255
+ }
256
+ } ,
257
+ {
258
+ "arcjet:rate-limit/storage" : {
259
+ get ( key ) {
260
+ return rateLimitCache . get ( key ) ;
261
+ } ,
262
+ set ( key , value , ttl ) {
263
+ rateLimitCache . set ( key , value , ttl ) ;
264
+ } ,
265
+ } ,
266
+ "arcjet:rate-limit/time" : {
267
+ now ( ) {
268
+ return nowInSeconds ( ) ;
269
+ } ,
270
+ } ,
271
+ } ,
272
+ ) ;
273
+
274
+ const resultJson = abc . fixedWindow ( configJson , requestJson ) ;
275
+ // TODO: Try/catch and Validate
276
+ return JSON . parse ( resultJson ) ;
277
+ }
0 commit comments