@mateosuarezdev/brpc 1.0.79 → 1.0.80

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # brpc
2
2
 
3
- A type-safe, batteries-included RPC framework for Bun. End-to-end type safety like tRPC, with built-in support for WebSocket subscriptions, file serving, media streaming, form uploads, server-side rendering, and more.
3
+ A type-safe, batteries-included RPC framework for Bun. End-to-end type safety like tRPC, with built-in support for WebSocket subscriptions, file serving, media streaming, form uploads, server-side rendering, automatic image optimization, on the fly optimized image serving, robust and simple storage api to use with S3, Auth service with passord, OTP (email, phone), Oauth, Cookies management, Client side headers management, and much more.
4
4
 
5
5
  ```
6
6
  npm install @mateosuarezdev/brpc zod
@@ -97,13 +97,13 @@ export const context = createContext(async (req) => {});
97
97
 
98
98
  Every handler receives a fully typed `ctx` with:
99
99
 
100
- | Field | Type | Description |
101
- |---|---|---|
102
- | `req` | `Request` | The raw Bun request |
103
- | `params` | `Record<string, string>` | URL path parameters |
104
- | `headers` | `Headers` | Response headers you can set |
105
- | `publishToProcedure` | `fn` | Publish to a subscription procedure |
106
- | *your fields* | *inferred* | Whatever you return from `createContext` |
100
+ | Field | Type | Description |
101
+ | -------------------- | ------------------------ | ---------------------------------------- |
102
+ | `req` | `Request` | The raw Bun request |
103
+ | `params` | `Record<string, string>` | URL path parameters |
104
+ | `headers` | `Headers` | Response headers you can set |
105
+ | `publishToProcedure` | `fn` | Publish to a subscription procedure |
106
+ | _your fields_ | _inferred_ | Whatever you return from `createContext` |
107
107
 
108
108
  ---
109
109
 
@@ -152,9 +152,11 @@ POST with `multipart/form-data`. Useful for file uploads. Pair with `createFileS
152
152
  import { createFileSchema } from "@mateosuarezdev/brpc";
153
153
 
154
154
  const uploadAvatar = procedure
155
- .input(z.object({
156
- file: createFileSchema({ acceptedTypes: { image: "*" }, maxSize: 5 }),
157
- }))
155
+ .input(
156
+ z.object({
157
+ file: createFileSchema({ acceptedTypes: { image: "*" }, maxSize: 5 }),
158
+ }),
159
+ )
158
160
  .formMutation(async ({ ctx, input }) => {
159
161
  await saveFile(input.file);
160
162
  return { success: true };
@@ -215,7 +217,11 @@ const messages = procedure
215
217
  Publish from anywhere on the server:
216
218
 
217
219
  ```ts
218
- router.publish(messages, { roomId: "general", text: "hello" }, { roomId: "general" });
220
+ router.publish(
221
+ messages,
222
+ { roomId: "general", text: "hello" },
223
+ { roomId: "general" },
224
+ );
219
225
  ```
220
226
 
221
227
  Parameters in the route path (e.g. `":roomId"`) scope the subscription topic — subscribers only receive messages matching their params.
@@ -235,7 +241,8 @@ import { context } from "./context";
235
241
  // Pass context for automatic type inference
236
242
  const authMiddleware = createMiddleware(context, async (ctx) => {
237
243
  const session = await getSession(ctx.req);
238
- if (!session) throw new BRPCError({ code: "UNAUTHORIZED", message: "Not authenticated" });
244
+ if (!session)
245
+ throw new BRPCError({ code: "UNAUTHORIZED", message: "Not authenticated" });
239
246
  return { session };
240
247
  });
241
248
 
@@ -260,12 +267,12 @@ Chain multiple middlewares — each one's return type accumulates into `ctx`:
260
267
 
261
268
  ```ts
262
269
  const localizedProtectedProcedure = procedure
263
- .use(authMiddleware) // ctx gains { session }
264
- .use(langMiddleware); // ctx gains { lang }
270
+ .use(authMiddleware) // ctx gains { session }
271
+ .use(langMiddleware); // ctx gains { lang }
265
272
 
266
273
  const getContent = localizedProtectedProcedure.query(async ({ ctx }) => {
267
- ctx.session; // typed
268
- ctx.lang; // typed
274
+ ctx.session; // typed
275
+ ctx.lang; // typed
269
276
  });
270
277
  ```
271
278
 
@@ -288,9 +295,9 @@ const adminMiddleware = createMiddleware(context, async (ctx) => {
288
295
  import { createRateLimiter } from "@mateosuarezdev/brpc";
289
296
 
290
297
  const rateLimiter = createRateLimiter({
291
- windowMs: 60_000, // 1 minute window
292
- maxRequests: 100, // max 100 requests per window
293
- maxEntries: 10_000, // max IPs to track
298
+ windowMs: 60_000, // 1 minute window
299
+ maxRequests: 100, // max 100 requests per window
300
+ maxEntries: 10_000, // max IPs to track
294
301
  message: "Too many requests",
295
302
  statusCode: 429,
296
303
  headerPrefix: "X-RateLimit",
@@ -391,7 +398,7 @@ createRouter({
391
398
  routes,
392
399
  globalMiddlewares: [
393
400
  async (ctx) => {
394
- ctx.session = await getSession(ctx.req) ?? null; // typed, works fine
401
+ ctx.session = (await getSession(ctx.req)) ?? null; // typed, works fine
395
402
  },
396
403
  ],
397
404
  });
@@ -401,19 +408,19 @@ At runtime, returning an object from a global middleware also works and merges i
401
408
 
402
409
  ### RouterConfig Options
403
410
 
404
- | Option | Type | Description |
405
- |---|---|---|
406
- | `context` | `fn` | Context creator — receives `Request`, returns your custom data |
407
- | `routes` | `Routes` | Nested route object |
408
- | `globalMiddlewares` | `Middleware[]` | Run before every request |
409
- | `prefix` | `string` | Path prefix for all routes |
410
- | `debug` | `boolean` | Enable route debug logging |
411
- | `autoFileCacheControl` | `boolean` | Auto-set cache headers for file procedures |
412
- | `websocket.onOpen` | `fn` | Called when a WebSocket connection opens |
413
- | `websocket.onClose` | `fn` | Called when a WebSocket connection closes |
414
- | `integrations.betterAuth` | `{ handler }` | Delegate `better-auth` routes to its handler |
415
- | `integrations.rawRoutes` | `Record<path, fn>` | Escape hatch for raw Bun routes |
416
- | `onError` | `fn` | Global error handler |
411
+ | Option | Type | Description |
412
+ | ------------------------- | ------------------ | -------------------------------------------------------------- |
413
+ | `context` | `fn` | Context creator — receives `Request`, returns your custom data |
414
+ | `routes` | `Routes` | Nested route object |
415
+ | `globalMiddlewares` | `Middleware[]` | Run before every request |
416
+ | `prefix` | `string` | Path prefix for all routes |
417
+ | `debug` | `boolean` | Enable route debug logging |
418
+ | `autoFileCacheControl` | `boolean` | Auto-set cache headers for file procedures |
419
+ | `websocket.onOpen` | `fn` | Called when a WebSocket connection opens |
420
+ | `websocket.onClose` | `fn` | Called when a WebSocket connection closes |
421
+ | `integrations.betterAuth` | `{ handler }` | Delegate `better-auth` routes to its handler |
422
+ | `integrations.rawRoutes` | `Record<path, fn>` | Escape hatch for raw Bun routes |
423
+ | `onError` | `fn` | Global error handler |
417
424
 
418
425
  ### Dynamic Route Parameters
419
426
 
@@ -422,11 +429,10 @@ Prefix a segment with `:` to capture it as a param:
422
429
  ```ts
423
430
  const routes = {
424
431
  users: {
425
- ":id": procedure
426
- .query(async ({ ctx }) => {
427
- ctx.params.id; // the captured value
428
- return getUser(ctx.params.id);
429
- }),
432
+ ":id": procedure.query(async ({ ctx }) => {
433
+ ctx.params.id; // the captured value
434
+ return getUser(ctx.params.id);
435
+ }),
430
436
  },
431
437
  };
432
438
  ```
@@ -446,7 +452,7 @@ const slowQuery = procedure
446
452
 
447
453
  ```ts
448
454
  const response = await router.testRequest(
449
- new Request("http://localhost/hello?input=%7B%22name%22%3A%22world%22%7D")
455
+ new Request("http://localhost/hello?input=%7B%22name%22%3A%22world%22%7D"),
450
456
  );
451
457
  ```
452
458
 
@@ -495,16 +501,16 @@ unsubscribe();
495
501
 
496
502
  ### BrpcClientOptions
497
503
 
498
- | Option | Type | Description |
499
- |---|---|---|
500
- | `headers` | `fn \| Headers` | Default headers for all requests |
501
- | `fetch` | `typeof fetch` | Custom fetch implementation |
502
- | `WebSocket` | `typeof WebSocket` | Custom WebSocket implementation |
503
- | `prefix` | `string` | API path prefix |
504
- | `apiPrefix` | `string` | Additional API prefix |
505
- | `s3Endpoint` | `string` | **Required.** S3/CDN endpoint for resolving storage object URLs |
506
- | `nodeEnv` | `string` | **Required.** Current environment (`"development"` \| `"production"`) |
507
- | `debug` | `boolean` | Enable client-side debug logging |
504
+ | Option | Type | Description |
505
+ | ------------ | ------------------ | --------------------------------------------------------------------- |
506
+ | `headers` | `fn \| Headers` | Default headers for all requests |
507
+ | `fetch` | `typeof fetch` | Custom fetch implementation |
508
+ | `WebSocket` | `typeof WebSocket` | Custom WebSocket implementation |
509
+ | `prefix` | `string` | API path prefix |
510
+ | `apiPrefix` | `string` | Additional API prefix |
511
+ | `s3Endpoint` | `string` | **Required.** S3/CDN endpoint for resolving storage object URLs |
512
+ | `nodeEnv` | `string` | **Required.** Current environment (`"development"` \| `"production"`) |
513
+ | `debug` | `boolean` | Enable client-side debug logging |
508
514
 
509
515
  ### Cache Keys
510
516
 
@@ -569,14 +575,14 @@ try {
569
575
  await client.routes.auth.login.mutation({ email, password });
570
576
  } catch (err) {
571
577
  if (err instanceof BrpcClientError) {
572
- err.status; // HTTP status code
573
- err.code; // BRPCErrorCode string
574
- err.clientCode; // custom client code if set
575
- err.isUnauthorized(); // status === 401
576
- err.isForbidden(); // status === 403
577
- err.isNotFound(); // status === 404
578
- err.isValidationError(); // status === 400
579
- err.isClientError("INVALID_CREDS"); // clientCode === "INVALID_CREDS"
578
+ err.status; // HTTP status code
579
+ err.code; // BRPCErrorCode string
580
+ err.clientCode; // custom client code if set
581
+ err.isUnauthorized(); // status === 401
582
+ err.isForbidden(); // status === 403
583
+ err.isNotFound(); // status === 404
584
+ err.isValidationError(); // status === 400
585
+ err.isClientError("INVALID_CREDS"); // clientCode === "INVALID_CREDS"
580
586
  }
581
587
  }
582
588
  ```
@@ -620,7 +626,7 @@ z.object({
620
626
  acceptedTypes: {
621
627
  image: ["image/jpeg", "image/png"],
622
628
  },
623
- maxSize: 5, // MB
629
+ maxSize: 5, // MB
624
630
  minSize: 0.1, // MB
625
631
  messages: {
626
632
  type: "Only JPEG and PNG are allowed",
@@ -636,7 +642,7 @@ z.object({
636
642
  audio: createFileSchema({
637
643
  acceptedTypes: { audio: "*" },
638
644
  }),
639
- })
645
+ });
640
646
  ```
641
647
 
642
648
  **Accepted type groups:** `image`, `video`, `audio`, `document`
@@ -669,17 +675,17 @@ throw BRPCError.internalServerError();
669
675
 
670
676
  ### Error Codes and HTTP Status Mapping
671
677
 
672
- | Code | Status |
673
- |---|---|
674
- | `BAD_REQUEST` | 400 |
675
- | `UNAUTHORIZED` | 401 |
676
- | `FORBIDDEN` | 403 |
677
- | `NOT_FOUND` | 404 |
678
- | `CONFLICT` | 409 |
679
- | `UNPROCESSABLE_CONTENT` | 422 |
680
- | `TOO_MANY_REQUESTS` | 429 |
681
- | `INTERNAL_SERVER_ERROR` | 500 |
682
- | `SERVICE_UNAVAILABLE` | 503 |
678
+ | Code | Status |
679
+ | ----------------------- | ------ |
680
+ | `BAD_REQUEST` | 400 |
681
+ | `UNAUTHORIZED` | 401 |
682
+ | `FORBIDDEN` | 403 |
683
+ | `NOT_FOUND` | 404 |
684
+ | `CONFLICT` | 409 |
685
+ | `UNPROCESSABLE_CONTENT` | 422 |
686
+ | `TOO_MANY_REQUESTS` | 429 |
687
+ | `INTERNAL_SERVER_ERROR` | 500 |
688
+ | `SERVICE_UNAVAILABLE` | 503 |
683
689
 
684
690
  Zod validation errors are automatically caught and returned as `BAD_REQUEST` (400) with field-level detail.
685
691
 
@@ -696,8 +702,8 @@ const video = procedure.fileStream(async ({ ctx }) => {
696
702
  const file = Bun.file(`./media/${ctx.params.filename}`);
697
703
 
698
704
  return streamMedia(file, ctx.req, {
699
- maxChunkSize: 2 * 1024 * 1024, // 2MB per chunk (default)
700
- cacheMaxAge: 3600, // Cache-Control max-age in seconds
705
+ maxChunkSize: 2 * 1024 * 1024, // 2MB per chunk (default)
706
+ cacheMaxAge: 3600, // Cache-Control max-age in seconds
701
707
  acceptedExtensions: [".mp4", ".webm", ".ogg"],
702
708
  });
703
709
  });
@@ -729,10 +735,10 @@ type UserOutput = AppOutput["users"]["getById"];
729
735
 
730
736
  ## Environment Variables
731
737
 
732
- | Variable | Default | Description |
733
- |---|---|---|
734
- | `MAX_WS_CONNECTIONS` | `1000` | Max simultaneous WebSocket connections |
735
- | `WS_TIMEOUT` | `30000` | WebSocket idle timeout in ms |
736
- | `MAX_REQUEST_SIZE` | `10485760` | Max request body size in bytes (10MB) |
737
- | `REQUEST_TIMEOUT` | `30000` | Default procedure timeout in ms |
738
- | `ROUTE_CACHE_SIZE` | `1000` | Route matcher cache size |
738
+ | Variable | Default | Description |
739
+ | -------------------- | ---------- | -------------------------------------- |
740
+ | `MAX_WS_CONNECTIONS` | `1000` | Max simultaneous WebSocket connections |
741
+ | `WS_TIMEOUT` | `30000` | WebSocket idle timeout in ms |
742
+ | `MAX_REQUEST_SIZE` | `10485760` | Max request body size in bytes (10MB) |
743
+ | `REQUEST_TIMEOUT` | `30000` | Default procedure timeout in ms |
744
+ | `ROUTE_CACHE_SIZE` | `1000` | Route matcher cache size |
@@ -1,4 +1,7 @@
1
1
  // @bun
2
- import*as v from"jsonwebtoken";import{RecordId as L,DateTime as I,Table as q,eq as y,or as u,surql as W}from"surrealdb";import*as X from"bcryptjs";import c from"node-cron";import a from"crypto";class Q extends Error{code;clientCode;httpStatus;data;cause;static STATUS_MAP={BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_SUPPORTED:405,TIMEOUT:408,CONFLICT:409,PRECONDITION_FAILED:412,PAYLOAD_TOO_LARGE:413,UNPROCESSABLE_CONTENT:422,TOO_MANY_REQUESTS:429,CLIENT_CLOSED_REQUEST:499,INTERNAL_SERVER_ERROR:500,NOT_IMPLEMENTED:501,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504};constructor($){super($.message);if(this.name="BRPCError",this.code=$.code,this.clientCode=$.clientCode,this.httpStatus=Q.STATUS_MAP[$.code],this.data=$.data,this.cause=$.cause,Error.captureStackTrace)Error.captureStackTrace(this,Q)}toJSON(){return{name:this.name,code:this.code,clientCode:this.clientCode,message:this.message,data:this.data,httpStatus:this.httpStatus}}static badRequest($,K,j){return new Q({code:"BAD_REQUEST",message:$,clientCode:K,data:j})}static unauthorized($="Unauthorized",K,j){return new Q({code:"UNAUTHORIZED",message:$,clientCode:K,data:j})}static forbidden($="Forbidden",K,j){return new Q({code:"FORBIDDEN",message:$,clientCode:K,data:j})}static notFound($="Not Found",K,j){return new Q({code:"NOT_FOUND",message:$,clientCode:K,data:j})}static preconditionFailed($="Precondition failed",K,j){return new Q({code:"NOT_FOUND",message:$,clientCode:K,data:j})}static conflict($,K,j){return new Q({code:"CONFLICT",message:$,clientCode:K,data:j})}static unprocessableContent($,K,j){return new Q({code:"UNPROCESSABLE_CONTENT",message:$,clientCode:K,data:j})}static tooManyRequests($="Too many requests",K,j){return new Q({code:"TOO_MANY_REQUESTS",message:$,clientCode:K,data:j})}static internalServerError($="Internal Server Error",K,j){return new Q({code:"INTERNAL_SERVER_ERROR",message:$,clientCode:K,data:j})}static timeout($="Request timeout",K,j){return new Q({code:"TIMEOUT",message:$,clientCode:K,data:j})}}var O={ACCESS_TOKEN:"access_token",REFRESH_TOKEN:"refresh_token",SESSION_ID:"session_id"},P={httpOnly:!0,sameSite:"Lax",path:"/",maxAge:1209600};function w($){return Object.entries($).map(([K,j])=>{if(j===void 0||j===null)return"";switch(K){case"maxAge":return`Max-Age=${j}`;case"httpOnly":return j?"HttpOnly":"";case"secure":return j?"Secure":"";case"sameSite":return`SameSite=${j}`;case"domain":return`Domain=${j}`;case"path":return`Path=${j}`;default:return`${K}=${j}`}}).filter(Boolean).join("; ")}function o($,K,j){let F={...P,secure:j};[`${O.ACCESS_TOKEN}=${K.accessToken}; ${w(F)}`,`${O.REFRESH_TOKEN}=${K.refreshToken}; ${w(F)}`,`${O.SESSION_ID}=${K.sessionId}; ${w({...F,maxAge:86400})}`].forEach((G)=>$.append("Set-Cookie",G))}function r($,K){let j={...P,secure:K,maxAge:0};[O.ACCESS_TOKEN,O.REFRESH_TOKEN,O.SESSION_ID].forEach((F)=>$.append("Set-Cookie",`${F}=; ${w(j)}`))}function T($){if(!$)return{};return $.split(";").reduce((K,j)=>{let F=j.trim().split("="),G=F[0],J=F.slice(1).join("=");if(G&&J)K[G]=J;return K},{})}var n=10,i=3,g=15,t=3,s=["cf-connecting-ip","x-real-ip","x-client-ip","x-forwarded","forwarded-for","x-forwarded-host"],b=new q("users"),U=new q("user_identities"),D=new q("sessions"),C=new q("otp_codes");function Y($){return`${$.table}:${$.id}`}function N($=new Date){return new I($)}class E{db;defaultSignupRoles;constructor($,K){this.db=$;this.defaultSignupRoles=K}async findByEmail($){let[K]=await this.db.select(b).where(y("email",$)).limit(1);return K}async findByPhone($){let[K]=await this.db.select(b).where(y("phone",$)).limit(1);return K}async findByContact($){if(!$.email&&!$.phone)return;let K=[];if($.email)K.push(y("email",$.email));if($.phone)K.push(y("phone",$.phone));let[j]=await this.db.select(b).where(u(...K)).limit(1);return j}async findById($){let[K,j]=$.split(":");return await this.db.select(new L(K,j))??void 0}async create({email:$,phone:K,options:j,ctx:F=this.db}){let[G]=await F.create(b).content({email:$??null,phone:K??null,app_metadata:{...j?.appMetadata,roles:this.defaultSignupRoles},last_sign_in_at:null});if(!G)throw Q.internalServerError("Failed to create user","USER_CREATE_FAILED");return G}async updateAppMetadata($,K){let j=await this.findById($);if(!j)throw Q.notFound("User not found","USER_NOT_FOUND");await this.db.update(j.id).merge({app_metadata:K})}async updateUserMetadata($,K){let j=await this.findById($);if(!j)throw Q.notFound("User not found","USER_NOT_FOUND");await this.db.update(j.id).merge({user_metadata:K})}async updateLastSignIn($,K=this.db){let[j,F]=$.split(":");await K.update(new L(j,F)).merge({last_sign_in_at:N()})}}class S{db;constructor($){this.db=$}async findByProvider($,K){let[j]=await this.db.query(W`SELECT * FROM type::table(${U}) WHERE provider = ${$} AND provider_id = ${K} LIMIT 1`);return j?.[0]}async findByUserId($){let[K,j]=$.split(":"),F=new L(K,j),[G]=await this.db.query(W`SELECT * FROM type::table(${U}) WHERE user_id = ${F}`);return G??[]}async findPasswordIdentity($){let[K,j]=$.split(":"),F=new L(K,j),[G]=await this.db.query(W`SELECT * FROM type::table(${U}) WHERE user_id = ${F} AND identity_data.password_hash != NONE LIMIT 1`);return G?.[0]}async create({userId:$,provider:K,providerId:j,identityData:F={},ctx:G=this.db}){let[J]=await G.create(U).content({user_id:$,provider:K,provider_id:j,identity_data:F});if(!J)throw Q.internalServerError("Failed to create identity","IDENTITY_CREATE_FAILED");return J}async updateData($,K){await this.db.update($).merge({identity_data:K})}}class m{db;debug;constructor($,K){this.db=$;this.debug=K}async create({userId:$,req:K,ctx:j=this.db}){let F=this.getClientIP(K),G=K.headers.get("user-agent"),[J,V]=$.split(":"),A=new L(J,V),[Z]=await j.create(D).content({user_id:A,ip_address:F,user_agent:G,invalidated_at:null});if(!Z)throw Q.internalServerError("Failed to create session","SESSION_CREATE_FAILED");return k(Z)}async get($){let[K,j]=$.split(":"),F=await this.db.select(new L(K,j));if(!F||F.invalidated_at)return;return k(F)}async getUserSessions($){let[K,j]=$.split(":"),F=new L(K,j),[G]=await this.db.query(W`SELECT * FROM type::table(${D}) WHERE user_id = ${F} AND invalidated_at = null`);return(G??[]).map((J)=>({id:Y(J.id),ipAddress:J.ip_address,userAgent:J.user_agent,invalidatedAt:J.invalidated_at}))}async invalidate($){let[K,j]=$.split(":");await this.db.update(new L(K,j)).merge({invalidated_at:N()})}async invalidateAllForUser($){let[K,j]=$.split(":"),F=new L(K,j);await this.db.query(W`UPDATE type::table(${D}) SET invalidated_at = time::now() WHERE user_id = ${F} AND invalidated_at = null`)}async invalidateAllExceptCurrent($,K){let[j,F]=$.split(":"),[G,J]=K.split(":"),V=new L(j,F),A=new L(G,J);await this.db.query(W`UPDATE type::table(${D}) SET invalidated_at = time::now() WHERE user_id = ${V} AND id != ${A} AND invalidated_at = null`)}async cleanup(){let[$]=await this.db.query(W`DELETE type::table(${D}) WHERE invalidated_at != NONE AND invalidated_at != null RETURN BEFORE`);return $?.length??0}getClientIP($){let K=$.headers.get("x-forwarded-for");if(K)return K.split(",")[0].trim();for(let j of s){let F=$.headers.get(j);if(F)return F.trim()}return this.debug?"127.0.0.1":null}}class h{db;otpProvider;constructor($,K){this.db=$;this.otpProvider=K}async checkRateLimit($){let K=new Date;K.setMinutes(K.getMinutes()-g);let j=[];if($.email)j.push(W`email = ${$.email}`);if($.phone)j.push(W`phone = ${$.phone}`);let F=j.length===1?j[0]:W`(${j[0]} OR ${j[1]})`,[G]=await this.db.query(W`SELECT * FROM type::table(${C}) WHERE (${F}) AND created_at >= ${N(K)}`);if((G?.length??0)>=t)throw Q.tooManyRequests(`Too many OTP requests. Please wait ${g} minutes.`,"OTP_RATE_LIMIT")}async generate({user:$,type:K,signupOptions:j}){await this.checkRateLimit({email:$.email,phone:$.phone});let F=a.randomInt(1e5,999999).toString(),G=await X.hash(F,10),J=new Date;J.setMinutes(J.getMinutes()+n);let V=$.id?(()=>{let[Z,_]=$.id.split(":");return new L(Z,_)})():null,[A]=await this.db.create(C).content({user_id:V,email:$.email??null,phone:$.phone??null,code:F,hashed_code:G,type:K,signup_options:j??null,expires_at:N(J),attempts:0,verified_at:null});if(!A)throw Q.internalServerError("Failed to create OTP record","OTP_CREATE_FAILED");try{await this.sendOTP({email:$.email,phone:$.phone,code:F})}catch{throw await this.db.delete(A.id),Q.internalServerError("Failed to send OTP","OTP_SEND_FAILED")}return{otpId:Y(A.id),expiresAt:J.toISOString(),message:`OTP sent to ${$.email?"email":"phone"}`}}async verify($,K){let[j,F]=$.split(":"),G=await this.db.select(new L(j,F));if(!G)throw Q.notFound("Invalid OTP","INVALID_OTP");if(new Date>new Date(G.expires_at))throw Q.badRequest("OTP has expired","OTP_EXPIRED");if(G.verified_at)throw Q.badRequest("OTP already used","OTP_ALREADY_USED");if(G.attempts>=i)throw Q.badRequest("Too many failed attempts","OTP_MAX_ATTEMPTS");if(!await X.compare(K,G.hashed_code))throw await this.db.update(G.id).merge({attempts:G.attempts+1}),Q.badRequest("Invalid OTP code","INVALID_OTP_CODE");return await this.db.update(G.id).merge({verified_at:N()}),G}async cleanup(){let $=N(),[K]=await this.db.query(W`DELETE type::table(${C}) WHERE expires_at < ${$} OR verified_at != NONE RETURN BEFORE`);return K?.length??0}async sendOTP({email:$,phone:K,code:j}){if($&&this.otpProvider?.sendEmailOTP)await this.otpProvider.sendEmailOTP({email:$,code:j});if(K&&this.otpProvider?.sendSMSOTP)await this.otpProvider.sendSMSOTP({phone:K,code:j})}}class R{jwtSecret;jwtRefreshSecret;accessTokenExpiry;constructor($,K,j){this.jwtSecret=$;this.jwtRefreshSecret=K;this.accessTokenExpiry=j}generateAccessToken($){return v.sign($,this.jwtSecret,{expiresIn:this.accessTokenExpiry})}generateRefreshToken($,K){return v.sign({sub:$,sessionId:K},this.jwtRefreshSecret)}verifyAccessToken($){return v.verify($,this.jwtSecret)}verifyRefreshToken($){return v.verify($,this.jwtRefreshSecret)}}function f($){return{id:Y($.id),email:$.email,phone:$.phone,appMetadata:$.app_metadata,userMetadata:$.user_metadata}}function k($){return{id:Y($.id),userId:Y($.user_id),ipAddress:$.ip_address,userAgent:$.user_agent,invalidatedAt:$.invalidated_at}}class p{userManager;identityManager;sessionManager;otpManager;tokenManager;cache;config;constructor($){this.config=$,this.userManager=new E($.db,$.defaultSignupRoles),this.identityManager=new S($.db),this.sessionManager=new m($.db,$.debug??!1),this.otpManager=new h($.db,$.otpProvider),this.tokenManager=new R($.secrets.jwt,$.secrets.jwtRefresh,$.accessTokenExpiry??"24h"),this.cache=$.cache?new $.cache({ttl:900000,maxSize:500,cleanupInterval:300000}):null}async initialize(){if(!this.config.disableCleanupCron)this.initializeCleanup()}async signUp($,K){this.validateSignUpMethod($);let{provider:j,providerId:F}=this.extractIdentityKey($);if(await this.identityManager.findByProvider(j,F))throw Q.conflict("User already exists","USER_ALREADY_EXISTS");let J=this.extractContact($);if($.passwordless||!$.password)return this.startPasswordlessSignUp(J,$.options);return this.completePasswordSignUp(j,F,J,$.password,K,$.options)}async signIn($,K){this.validateSignInMethod($);let{provider:j,providerId:F}=this.extractIdentityKey($),G=await this.identityManager.findByProvider(j,F);if(!G)throw Q.notFound(`No account found with this ${$.type}`,"USER_NOT_FOUND");if($.passwordless||!$.password)return this.startPasswordlessSignIn(G);return this.completePasswordSignIn(G,$.password,K)}async verifyOTP($,K,j){let F=await this.otpManager.verify($,K);if(F.type==="signup")return this.completePasswordlessSignUp(F,j);if(F.type==="signin")return this.completePasswordlessSignIn(F,j);throw Q.badRequest("Invalid OTP type","INVALID_OTP_TYPE")}async signOut($){await this.sessionManager.invalidate($)}async signOutAll($){await this.sessionManager.invalidateAllForUser($)}async signOutAllExcept($,K){await this.sessionManager.invalidateAllExceptCurrent($,K)}async getUserSessions($){return this.sessionManager.getUserSessions($)}async refreshTokens($){let K=this.tokenManager.verifyRefreshToken($),j=await this.sessionManager.get(K.sessionId);if(!j)throw Q.notFound("Session not found","SESSION_NOT_FOUND");if(j.userId!==K.sub)throw Q.conflict("Session mismatch","SESSION_USER_MISMATCH");let F=await this.userManager.findById(K.sub);if(!F)throw Q.notFound("User not found","USER_NOT_FOUND");let G=this.tokenManager.generateAccessToken({sub:Y(F.id),sessionId:j.id,appMetadata:F.app_metadata,userMetadata:F.user_metadata}),J=this.tokenManager.generateRefreshToken(Y(F.id),j.id);return{accessToken:G,refreshToken:J,sessionId:j.id,user:f(F)}}async verifyToken($){try{if(!$||!$.startsWith("Bearer "))return null;let K=$.split(" ")[1],j=this.tokenManager.verifyAccessToken(K),F=await this.sessionManager.get(j.sessionId);if(!F||F.userId!==j.sub)return null;let G=await this.userManager.findById(j.sub);if(!G)return null;return{sub:j.sub,sessionId:j.sessionId,appMetadata:G.app_metadata,userMetadata:G.user_metadata,exp:j.exp}}catch{return null}}async fastVerifyToken($){try{if(!$||!$.startsWith("Bearer "))return null;let K=$.split(" ")[1];return this.tokenManager.verifyAccessToken(K)}catch{return null}}async verifyTokenFromCookie($){if(!$)return null;let j=T($)[O.ACCESS_TOKEN];if(!j)return null;return this.verifyToken(`Bearer ${j}`)}async refreshTokenFromCookie($){if(!$)return null;let j=T($)[O.REFRESH_TOKEN];if(!j)return null;try{return await this.refreshTokens(j)}catch{return null}}async getUserById($){return this.userManager.findById($)}async getUserByContact($){return this.userManager.findByContact($)}async getUserIdentities($){return this.identityManager.findByUserId($)}async updateUserPassword($,K){let j=await this.identityManager.findPasswordIdentity($);if(!j)throw Q.notFound("No password identity found","NO_PASSWORD_SET");let F=await X.hash(K,10);await this.identityManager.updateData(j.id,{password_hash:F})}async updateAppMetadata($,K){return this.userManager.updateAppMetadata($,K)}async updateUserMetadata($,K){return this.userManager.updateUserMetadata($,K)}async changePassword($,K,j){let F=await this.identityManager.findPasswordIdentity($);if(!F)throw Q.badRequest("User has no password set","NO_PASSWORD_SET");if(!await X.compare(K,F.identity_data.password_hash))throw Q.badRequest("Current password is incorrect","INVALID_PASSWORD");let J=await X.hash(j,10);await this.identityManager.updateData(F.id,{password_hash:J})}async requestPasswordReset($){let K=$.email?"email":"phone",j=$.email??$.phone,F=await this.identityManager.findByProvider(K,j);if(!F)throw Q.notFound("User not found","USER_NOT_FOUND");let G=Y(F.user_id);return this.otpManager.generate({user:{id:G,email:$.email,phone:$.phone},type:"password_reset"})}async resetPassword($,K,j){let F=await this.otpManager.verify($,K);if(F.type!=="password_reset")throw Q.badRequest("Invalid OTP type for password reset","INVALID_OTP_TYPE");if(!F.user_id)throw Q.internalServerError("Invalid OTP record","INVALID_OTP_RECORD");let G=F.email?"email":"phone",J=F.email??F.phone,V=await this.identityManager.findByProvider(G,J);if(!V)throw Q.internalServerError("Identity not found","IDENTITY_NOT_FOUND");let A=await X.hash(j,10);await this.identityManager.updateData(V.id,{password_hash:A});let Z=Y(F.user_id);return await this.sessionManager.invalidateAllForUser(Z),{success:!0}}async adminCreateUser({email:$,phone:K,password:j,options:F}){let G=j?await X.hash(j,10):null,J=await this.config.db.beginTransaction();try{let[V]=await J.create(b).content({email:$??null,phone:K??null,app_metadata:{...F?.appMetadata,roles:this.config.defaultSignupRoles},last_sign_in_at:null});if(!V)throw Error("Failed to create user");if($||K){let A=$?"email":"phone",Z=$??K;await J.create(U).content({user_id:V.id,provider:A,provider_id:Z,identity_data:G?{password_hash:G}:{}})}return await J.commit(),f(V)}catch(V){throw await J.cancel(),V}}cacheSession($,K){this.cache?.set($,K)}getCachedSession($){return this.cache?.get($)??null}invalidateCachedSession($){return this.cache?.delete($)??!1}getCacheStats(){return this.cache?.stats()??null}clearCache(){this.cache?.clear()}isTokenNearExpiry($){return $-Math.floor(Date.now()/1000)<300}async testCreateUserWithSession({email:$,phone:K,password:j,options:F,req:G}){let J=j?await X.hash(j,10):null,V=this.sessionManager.getClientIP(G),A=await this.config.db.beginTransaction();try{let[Z]=await A.create(b).content({email:$??null,phone:K??null,app_metadata:{...F?.appMetadata,roles:this.config.defaultSignupRoles},last_sign_in_at:N()});if(!Z)throw Error("Failed to create user");if($||K){let B=$?"email":"phone",l=$??K;await A.create(U).content({user_id:Z.id,provider:B,provider_id:l,identity_data:J?{password_hash:J}:{}})}let[_]=await A.create(D).content({user_id:Z.id,ip_address:V,user_agent:G.headers.get("user-agent"),invalidated_at:null});if(!_)throw Error("Failed to create session");let H=Y(Z.id),z=Y(_.id),x=this.tokenManager.generateAccessToken({sub:H,sessionId:z,appMetadata:Z.app_metadata,userMetadata:Z.user_metadata}),M=this.tokenManager.generateRefreshToken(H,z);return await A.commit(),{user:f(Z),session:k(_),accessToken:x,refreshToken:M}}catch(Z){throw await A.cancel(),Z}}async startPasswordlessSignUp($,K){return this.otpManager.generate({user:{id:void 0,email:$.email,phone:$.phone},type:"signup",signupOptions:K})}async startPasswordlessSignIn($){let K=Y($.user_id),j=$.provider==="email"?$.provider_id:void 0,F=$.provider==="phone"?$.provider_id:void 0;return this.otpManager.generate({user:{id:K,email:j,phone:F},type:"signin"})}async completePasswordSignUp($,K,j,F,G,J){let V=await X.hash(F,10),A=await this.config.db.beginTransaction();try{let[Z]=await A.create(b).content({email:j.email??null,phone:j.phone??null,app_metadata:{...J?.appMetadata,roles:this.config.defaultSignupRoles},last_sign_in_at:N()});if(!Z)throw Error("Failed to create user");let[_]=await A.create(U).content({user_id:Z.id,provider:$,provider_id:K,identity_data:{password_hash:V}});if(!_)throw Error("Failed to create identity");let H=this.sessionManager.getClientIP(G),[z]=await A.create(D).content({user_id:Z.id,ip_address:H,user_agent:G.headers.get("user-agent"),invalidated_at:null});if(!z)throw Error("Failed to create session");let x=Y(Z.id),M=Y(z.id),B=this.tokenManager.generateAccessToken({sub:x,sessionId:M,appMetadata:Z.app_metadata,userMetadata:Z.user_metadata}),l=this.tokenManager.generateRefreshToken(x,M);return await A.commit(),{user:f(Z),session:k(z),accessToken:B,refreshToken:l}}catch(Z){throw await A.cancel(),Z}}async completePasswordSignIn($,K,j){let{password_hash:F}=$.identity_data;if(!F)throw Q.conflict("This account has no password. Use OTP to sign in.","NO_PASSWORD_SET");if(!await X.compare(K,F))throw Q.conflict("Invalid credentials","INVALID_CREDENTIALS");let J=Y($.user_id),V=await this.userManager.findById(J);if(!V)throw Q.notFound("User not found","USER_NOT_FOUND");return this.createAuthSession(V,j)}async completePasswordlessSignUp($,K){let j=$.email?"email":"phone",F=$.email??$.phone,G=$.signup_options,J=await this.config.db.beginTransaction();try{let[V]=await J.create(b).content({email:$.email??null,phone:$.phone??null,app_metadata:{...G?.appMetadata,roles:this.config.defaultSignupRoles},last_sign_in_at:N()});if(!V)throw Error("Failed to create user");let[A]=await J.create(U).content({user_id:V.id,provider:j,provider_id:F,identity_data:{}});if(!A)throw Error("Failed to create identity");let Z=this.sessionManager.getClientIP(K),[_]=await J.create(D).content({user_id:V.id,ip_address:Z,user_agent:K.headers.get("user-agent"),invalidated_at:null});if(!_)throw Error("Failed to create session");let H=Y(V.id),z=Y(_.id),x=this.tokenManager.generateAccessToken({sub:H,sessionId:z,appMetadata:V.app_metadata,userMetadata:V.user_metadata}),M=this.tokenManager.generateRefreshToken(H,z);return await J.commit(),{user:f(V),session:k(_),accessToken:x,refreshToken:M}}catch(V){throw await J.cancel(),V}}async completePasswordlessSignIn($,K){if(!$.user_id)throw Q.internalServerError("Invalid OTP record","INVALID_OTP_RECORD");let j=Y($.user_id),F=await this.userManager.findById(j);if(!F)throw Q.notFound("User not found","USER_NOT_FOUND");return this.createAuthSession(F,K)}async createAuthSession($,K){let j=await this.config.db.beginTransaction();try{let F=this.sessionManager.getClientIP(K),[G]=await j.create(D).content({user_id:$.id,ip_address:F,user_agent:K.headers.get("user-agent"),invalidated_at:null});if(!G)throw Error("Failed to create session");await j.update($.id).merge({last_sign_in_at:N()});let J=Y($.id),V=Y(G.id),A=this.tokenManager.generateAccessToken({sub:J,sessionId:V,appMetadata:$.app_metadata,userMetadata:$.user_metadata}),Z=this.tokenManager.generateRefreshToken(J,V);return await j.commit(),{user:f($),session:k(G),accessToken:A,refreshToken:Z}}catch(F){throw await j.cancel(),F}}validateSignUpMethod($){if($.type==="email")this.validateEmail($.email);else $.phone=this.normalizePhoneNumber($.phone)}validateSignInMethod($){if($.type==="email")this.validateEmail($.email);else $.phone=this.normalizePhoneNumber($.phone)}validateEmail($){if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test($))throw Q.badRequest("Invalid email format","INVALID_EMAIL")}normalizePhoneNumber($){let K=$.replace(/\D/g,"");if(K.length===10)return`+1${K}`;if(K.length===11&&K.startsWith("1"))return`+${K}`;if(!K.startsWith("+"))return`+${K}`;return K}extractContact($){return $.type==="email"?{email:$.email}:{phone:$.phone}}extractIdentityKey($){return $.type==="email"?{provider:"email",providerId:$.email}:{provider:"phone",providerId:$.phone}}initializeCleanup(){Promise.all([this.sessionManager.cleanup(),this.otpManager.cleanup()]).catch(()=>{}),c.schedule("0 2 * * *",async()=>{await Promise.all([this.sessionManager.cleanup(),this.otpManager.cleanup()]).catch(()=>{})})}}function Q$($){return new p($)}export{o as setAuthCookies,T as parseCookies,Q$ as createAuth,r as clearAuthCookies,O as COOKIE_NAMES,p as AuthService};
2
+ import*as v from"jsonwebtoken";import{RecordId as L,DateTime as I,Table as q,eq as y,or as u,surql as W}from"surrealdb";import*as X from"bcryptjs";import c from"node-cron";import a from"crypto";class Q extends Error{code;clientCode;httpStatus;data;cause;static STATUS_MAP={BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_SUPPORTED:405,TIMEOUT:408,CONFLICT:409,PRECONDITION_FAILED:412,PAYLOAD_TOO_LARGE:413,UNPROCESSABLE_CONTENT:422,TOO_MANY_REQUESTS:429,CLIENT_CLOSED_REQUEST:499,INTERNAL_SERVER_ERROR:500,NOT_IMPLEMENTED:501,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504};constructor($){super($.message);if(this.name="BRPCError",this.code=$.code,this.clientCode=$.clientCode,this.httpStatus=Q.STATUS_MAP[$.code],this.data=$.data,this.cause=$.cause,Error.captureStackTrace)Error.captureStackTrace(this,Q)}toJSON(){return{name:this.name,code:this.code,clientCode:this.clientCode,message:this.message,data:this.data,httpStatus:this.httpStatus}}static badRequest($,K,j){return new Q({code:"BAD_REQUEST",message:$,clientCode:K,data:j})}static unauthorized($="Unauthorized",K,j){return new Q({code:"UNAUTHORIZED",message:$,clientCode:K,data:j})}static forbidden($="Forbidden",K,j){return new Q({code:"FORBIDDEN",message:$,clientCode:K,data:j})}static notFound($="Not Found",K,j){return new Q({code:"NOT_FOUND",message:$,clientCode:K,data:j})}static preconditionFailed($="Precondition failed",K,j){//! Intentionally maps to NOT_FOUND instead of PRECONDITION_FAILED — this was changed to fix a bug
3
+ //! but the exact cause is unknown. Suspected: some client/proxy/middleware misbehaved on 412
4
+ //! (not a browser auto-retry issue — 412 doesn't trigger those). Investigate before changing back.
5
+ return new Q({code:"NOT_FOUND",message:$,clientCode:K,data:j})}static conflict($,K,j){return new Q({code:"CONFLICT",message:$,clientCode:K,data:j})}static unprocessableContent($,K,j){return new Q({code:"UNPROCESSABLE_CONTENT",message:$,clientCode:K,data:j})}static tooManyRequests($="Too many requests",K,j){return new Q({code:"TOO_MANY_REQUESTS",message:$,clientCode:K,data:j})}static internalServerError($="Internal Server Error",K,j){return new Q({code:"INTERNAL_SERVER_ERROR",message:$,clientCode:K,data:j})}static timeout($="Request timeout",K,j){return new Q({code:"TIMEOUT",message:$,clientCode:K,data:j})}}var O={ACCESS_TOKEN:"access_token",REFRESH_TOKEN:"refresh_token",SESSION_ID:"session_id"},P={httpOnly:!0,sameSite:"Lax",path:"/",maxAge:1209600};function w($){return Object.entries($).map(([K,j])=>{if(j===void 0||j===null)return"";switch(K){case"maxAge":return`Max-Age=${j}`;case"httpOnly":return j?"HttpOnly":"";case"secure":return j?"Secure":"";case"sameSite":return`SameSite=${j}`;case"domain":return`Domain=${j}`;case"path":return`Path=${j}`;default:return`${K}=${j}`}}).filter(Boolean).join("; ")}function o($,K,j){let F={...P,secure:j};[`${O.ACCESS_TOKEN}=${K.accessToken}; ${w(F)}`,`${O.REFRESH_TOKEN}=${K.refreshToken}; ${w(F)}`,`${O.SESSION_ID}=${K.sessionId}; ${w({...F,maxAge:86400})}`].forEach((G)=>$.append("Set-Cookie",G))}function r($,K){let j={...P,secure:K,maxAge:0};[O.ACCESS_TOKEN,O.REFRESH_TOKEN,O.SESSION_ID].forEach((F)=>$.append("Set-Cookie",`${F}=; ${w(j)}`))}function T($){if(!$)return{};return $.split(";").reduce((K,j)=>{let F=j.trim().split("="),G=F[0],J=F.slice(1).join("=");if(G&&J)K[G]=J;return K},{})}var n=10,i=3,g=15,t=3,s=["cf-connecting-ip","x-real-ip","x-client-ip","x-forwarded","forwarded-for","x-forwarded-host"],U=new q("users"),b=new q("user_identities"),D=new q("sessions"),C=new q("otp_codes");function Y($){return`${$.table}:${$.id}`}function N($=new Date){return new I($)}class E{db;defaultSignupRoles;constructor($,K){this.db=$;this.defaultSignupRoles=K}async findByEmail($){let[K]=await this.db.select(U).where(y("email",$)).limit(1);return K}async findByPhone($){let[K]=await this.db.select(U).where(y("phone",$)).limit(1);return K}async findByContact($){if(!$.email&&!$.phone)return;let K=[];if($.email)K.push(y("email",$.email));if($.phone)K.push(y("phone",$.phone));let[j]=await this.db.select(U).where(u(...K)).limit(1);return j}async findById($){let[K,j]=$.split(":");return await this.db.select(new L(K,j))??void 0}async create({email:$,phone:K,options:j,ctx:F=this.db}){let[G]=await F.create(U).content({email:$??null,phone:K??null,app_metadata:{...j?.appMetadata,roles:this.defaultSignupRoles},last_sign_in_at:null});if(!G)throw Q.internalServerError("Failed to create user","USER_CREATE_FAILED");return G}async updateAppMetadata($,K){let j=await this.findById($);if(!j)throw Q.notFound("User not found","USER_NOT_FOUND");await this.db.update(j.id).merge({app_metadata:K})}async updateUserMetadata($,K){let j=await this.findById($);if(!j)throw Q.notFound("User not found","USER_NOT_FOUND");await this.db.update(j.id).merge({user_metadata:K})}async updateLastSignIn($,K=this.db){let[j,F]=$.split(":");await K.update(new L(j,F)).merge({last_sign_in_at:N()})}}class m{db;constructor($){this.db=$}async findByProvider($,K){let[j]=await this.db.query(W`SELECT * FROM type::table(${b}) WHERE provider = ${$} AND provider_id = ${K} LIMIT 1`);return j?.[0]}async findByUserId($){let[K,j]=$.split(":"),F=new L(K,j),[G]=await this.db.query(W`SELECT * FROM type::table(${b}) WHERE user_id = ${F}`);return G??[]}async findPasswordIdentity($){let[K,j]=$.split(":"),F=new L(K,j),[G]=await this.db.query(W`SELECT * FROM type::table(${b}) WHERE user_id = ${F} AND identity_data.password_hash != NONE LIMIT 1`);return G?.[0]}async create({userId:$,provider:K,providerId:j,identityData:F={},ctx:G=this.db}){let[J]=await G.create(b).content({user_id:$,provider:K,provider_id:j,identity_data:F});if(!J)throw Q.internalServerError("Failed to create identity","IDENTITY_CREATE_FAILED");return J}async updateData($,K){await this.db.update($).merge({identity_data:K})}}class S{db;debug;constructor($,K){this.db=$;this.debug=K}async create({userId:$,req:K,ctx:j=this.db}){let F=this.getClientIP(K),G=K.headers.get("user-agent"),[J,V]=$.split(":"),A=new L(J,V),[Z]=await j.create(D).content({user_id:A,ip_address:F,user_agent:G,invalidated_at:null});if(!Z)throw Q.internalServerError("Failed to create session","SESSION_CREATE_FAILED");return k(Z)}async get($){let[K,j]=$.split(":"),F=await this.db.select(new L(K,j));if(!F||F.invalidated_at)return;return k(F)}async getUserSessions($){let[K,j]=$.split(":"),F=new L(K,j),[G]=await this.db.query(W`SELECT * FROM type::table(${D}) WHERE user_id = ${F} AND invalidated_at = null`);return(G??[]).map((J)=>({id:Y(J.id),ipAddress:J.ip_address,userAgent:J.user_agent,invalidatedAt:J.invalidated_at}))}async invalidate($){let[K,j]=$.split(":");await this.db.update(new L(K,j)).merge({invalidated_at:N()})}async invalidateAllForUser($){let[K,j]=$.split(":"),F=new L(K,j);await this.db.query(W`UPDATE type::table(${D}) SET invalidated_at = time::now() WHERE user_id = ${F} AND invalidated_at = null`)}async invalidateAllExceptCurrent($,K){let[j,F]=$.split(":"),[G,J]=K.split(":"),V=new L(j,F),A=new L(G,J);await this.db.query(W`UPDATE type::table(${D}) SET invalidated_at = time::now() WHERE user_id = ${V} AND id != ${A} AND invalidated_at = null`)}async cleanup(){let[$]=await this.db.query(W`DELETE type::table(${D}) WHERE invalidated_at != NONE AND invalidated_at != null RETURN BEFORE`);return $?.length??0}getClientIP($){let K=$.headers.get("x-forwarded-for");if(K)return K.split(",")[0].trim();for(let j of s){let F=$.headers.get(j);if(F)return F.trim()}return this.debug?"127.0.0.1":null}}class h{db;otpProvider;constructor($,K){this.db=$;this.otpProvider=K}async checkRateLimit($){let K=new Date;K.setMinutes(K.getMinutes()-g);let j=[];if($.email)j.push(W`email = ${$.email}`);if($.phone)j.push(W`phone = ${$.phone}`);let F=j.length===1?j[0]:W`(${j[0]} OR ${j[1]})`,[G]=await this.db.query(W`SELECT * FROM type::table(${C}) WHERE (${F}) AND created_at >= ${N(K)}`);if((G?.length??0)>=t)throw Q.tooManyRequests(`Too many OTP requests. Please wait ${g} minutes.`,"OTP_RATE_LIMIT")}async generate({user:$,type:K,signupOptions:j}){await this.checkRateLimit({email:$.email,phone:$.phone});let F=a.randomInt(1e5,999999).toString(),G=await X.hash(F,10),J=new Date;J.setMinutes(J.getMinutes()+n);let V=$.id?(()=>{let[Z,_]=$.id.split(":");return new L(Z,_)})():null,[A]=await this.db.create(C).content({user_id:V,email:$.email??null,phone:$.phone??null,code:F,hashed_code:G,type:K,signup_options:j??null,expires_at:N(J),attempts:0,verified_at:null});if(!A)throw Q.internalServerError("Failed to create OTP record","OTP_CREATE_FAILED");try{await this.sendOTP({email:$.email,phone:$.phone,code:F})}catch{throw await this.db.delete(A.id),Q.internalServerError("Failed to send OTP","OTP_SEND_FAILED")}return{otpId:Y(A.id),expiresAt:J.toISOString(),message:`OTP sent to ${$.email?"email":"phone"}`}}async verify($,K){let[j,F]=$.split(":"),G=await this.db.select(new L(j,F));if(!G)throw Q.notFound("Invalid OTP","INVALID_OTP");if(new Date>new Date(G.expires_at))throw Q.badRequest("OTP has expired","OTP_EXPIRED");if(G.verified_at)throw Q.badRequest("OTP already used","OTP_ALREADY_USED");if(G.attempts>=i)throw Q.badRequest("Too many failed attempts","OTP_MAX_ATTEMPTS");if(!await X.compare(K,G.hashed_code))throw await this.db.update(G.id).merge({attempts:G.attempts+1}),Q.badRequest("Invalid OTP code","INVALID_OTP_CODE");return await this.db.update(G.id).merge({verified_at:N()}),G}async cleanup(){let $=N(),[K]=await this.db.query(W`DELETE type::table(${C}) WHERE expires_at < ${$} OR verified_at != NONE RETURN BEFORE`);return K?.length??0}async sendOTP({email:$,phone:K,code:j}){if($&&this.otpProvider?.sendEmailOTP)await this.otpProvider.sendEmailOTP({email:$,code:j});if(K&&this.otpProvider?.sendSMSOTP)await this.otpProvider.sendSMSOTP({phone:K,code:j})}}class R{jwtSecret;jwtRefreshSecret;accessTokenExpiry;constructor($,K,j){this.jwtSecret=$;this.jwtRefreshSecret=K;this.accessTokenExpiry=j}generateAccessToken($){return v.sign($,this.jwtSecret,{expiresIn:this.accessTokenExpiry})}generateRefreshToken($,K){return v.sign({sub:$,sessionId:K},this.jwtRefreshSecret)}verifyAccessToken($){return v.verify($,this.jwtSecret)}verifyRefreshToken($){return v.verify($,this.jwtRefreshSecret)}}function f($){return{id:Y($.id),email:$.email,phone:$.phone,appMetadata:$.app_metadata,userMetadata:$.user_metadata}}function k($){return{id:Y($.id),userId:Y($.user_id),ipAddress:$.ip_address,userAgent:$.user_agent,invalidatedAt:$.invalidated_at}}class p{userManager;identityManager;sessionManager;otpManager;tokenManager;cache;config;constructor($){this.config=$,this.userManager=new E($.db,$.defaultSignupRoles),this.identityManager=new m($.db),this.sessionManager=new S($.db,$.debug??!1),this.otpManager=new h($.db,$.otpProvider),this.tokenManager=new R($.secrets.jwt,$.secrets.jwtRefresh,$.accessTokenExpiry??"24h"),this.cache=$.cache?new $.cache({ttl:900000,maxSize:500,cleanupInterval:300000}):null}async initialize(){if(!this.config.disableCleanupCron)this.initializeCleanup()}async signUp($,K){this.validateSignUpMethod($);let{provider:j,providerId:F}=this.extractIdentityKey($);if(await this.identityManager.findByProvider(j,F))throw Q.conflict("User already exists","USER_ALREADY_EXISTS");let J=this.extractContact($);if($.passwordless||!$.password)return this.startPasswordlessSignUp(J,$.options);return this.completePasswordSignUp(j,F,J,$.password,K,$.options)}async signIn($,K){this.validateSignInMethod($);let{provider:j,providerId:F}=this.extractIdentityKey($),G=await this.identityManager.findByProvider(j,F);if(!G)throw Q.notFound(`No account found with this ${$.type}`,"USER_NOT_FOUND");if($.passwordless||!$.password)return this.startPasswordlessSignIn(G);return this.completePasswordSignIn(G,$.password,K)}async verifyOTP($,K,j){let F=await this.otpManager.verify($,K);if(F.type==="signup")return this.completePasswordlessSignUp(F,j);if(F.type==="signin")return this.completePasswordlessSignIn(F,j);throw Q.badRequest("Invalid OTP type","INVALID_OTP_TYPE")}async signOut($){await this.sessionManager.invalidate($)}async signOutAll($){await this.sessionManager.invalidateAllForUser($)}async signOutAllExcept($,K){await this.sessionManager.invalidateAllExceptCurrent($,K)}async getUserSessions($){return this.sessionManager.getUserSessions($)}async refreshTokens($){let K=this.tokenManager.verifyRefreshToken($),j=await this.sessionManager.get(K.sessionId);if(!j)throw Q.notFound("Session not found","SESSION_NOT_FOUND");if(j.userId!==K.sub)throw Q.conflict("Session mismatch","SESSION_USER_MISMATCH");let F=await this.userManager.findById(K.sub);if(!F)throw Q.notFound("User not found","USER_NOT_FOUND");let G=this.tokenManager.generateAccessToken({sub:Y(F.id),sessionId:j.id,appMetadata:F.app_metadata,userMetadata:F.user_metadata}),J=this.tokenManager.generateRefreshToken(Y(F.id),j.id);return{accessToken:G,refreshToken:J,sessionId:j.id,user:f(F)}}async verifyToken($){try{if(!$||!$.startsWith("Bearer "))return null;let K=$.split(" ")[1],j=this.tokenManager.verifyAccessToken(K),F=await this.sessionManager.get(j.sessionId);if(!F||F.userId!==j.sub)return null;let G=await this.userManager.findById(j.sub);if(!G)return null;return{sub:j.sub,sessionId:j.sessionId,appMetadata:G.app_metadata,userMetadata:G.user_metadata,exp:j.exp}}catch{return null}}async fastVerifyToken($){try{if(!$||!$.startsWith("Bearer "))return null;let K=$.split(" ")[1];return this.tokenManager.verifyAccessToken(K)}catch{return null}}async verifyTokenFromCookie($){if(!$)return null;let j=T($)[O.ACCESS_TOKEN];if(!j)return null;return this.verifyToken(`Bearer ${j}`)}async refreshTokenFromCookie($){if(!$)return null;let j=T($)[O.REFRESH_TOKEN];if(!j)return null;try{return await this.refreshTokens(j)}catch{return null}}async getUserById($){return this.userManager.findById($)}async getUserByContact($){return this.userManager.findByContact($)}async getUserIdentities($){return this.identityManager.findByUserId($)}async updateUserPassword($,K){let j=await this.identityManager.findPasswordIdentity($);if(!j)throw Q.notFound("No password identity found","NO_PASSWORD_SET");let F=await X.hash(K,10);await this.identityManager.updateData(j.id,{password_hash:F})}async updateAppMetadata($,K){return this.userManager.updateAppMetadata($,K)}async updateUserMetadata($,K){return this.userManager.updateUserMetadata($,K)}async changePassword($,K,j){let F=await this.identityManager.findPasswordIdentity($);if(!F)throw Q.badRequest("User has no password set","NO_PASSWORD_SET");if(!await X.compare(K,F.identity_data.password_hash))throw Q.badRequest("Current password is incorrect","INVALID_PASSWORD");let J=await X.hash(j,10);await this.identityManager.updateData(F.id,{password_hash:J})}async requestPasswordReset($){let K=$.email?"email":"phone",j=$.email??$.phone,F=await this.identityManager.findByProvider(K,j);if(!F)throw Q.notFound("User not found","USER_NOT_FOUND");let G=Y(F.user_id);return this.otpManager.generate({user:{id:G,email:$.email,phone:$.phone},type:"password_reset"})}async resetPassword($,K,j){let F=await this.otpManager.verify($,K);if(F.type!=="password_reset")throw Q.badRequest("Invalid OTP type for password reset","INVALID_OTP_TYPE");if(!F.user_id)throw Q.internalServerError("Invalid OTP record","INVALID_OTP_RECORD");let G=F.email?"email":"phone",J=F.email??F.phone,V=await this.identityManager.findByProvider(G,J);if(!V)throw Q.internalServerError("Identity not found","IDENTITY_NOT_FOUND");let A=await X.hash(j,10);await this.identityManager.updateData(V.id,{password_hash:A});let Z=Y(F.user_id);return await this.sessionManager.invalidateAllForUser(Z),{success:!0}}async adminCreateUser({email:$,phone:K,password:j,options:F}){let G=j?await X.hash(j,10):null,J=await this.config.db.beginTransaction();try{let[V]=await J.create(U).content({email:$??null,phone:K??null,app_metadata:{...F?.appMetadata,roles:this.config.defaultSignupRoles},last_sign_in_at:null});if(!V)throw Error("Failed to create user");if($||K){let A=$?"email":"phone",Z=$??K;await J.create(b).content({user_id:V.id,provider:A,provider_id:Z,identity_data:G?{password_hash:G}:{}})}return await J.commit(),f(V)}catch(V){throw await J.cancel(),V}}cacheSession($,K){this.cache?.set($,K)}getCachedSession($){return this.cache?.get($)??null}invalidateCachedSession($){return this.cache?.delete($)??!1}getCacheStats(){return this.cache?.stats()??null}clearCache(){this.cache?.clear()}isTokenNearExpiry($){return $-Math.floor(Date.now()/1000)<300}async testCreateUserWithSession({email:$,phone:K,password:j,options:F,req:G}){let J=j?await X.hash(j,10):null,V=this.sessionManager.getClientIP(G),A=await this.config.db.beginTransaction();try{let[Z]=await A.create(U).content({email:$??null,phone:K??null,app_metadata:{...F?.appMetadata,roles:this.config.defaultSignupRoles},last_sign_in_at:N()});if(!Z)throw Error("Failed to create user");if($||K){let B=$?"email":"phone",l=$??K;await A.create(b).content({user_id:Z.id,provider:B,provider_id:l,identity_data:J?{password_hash:J}:{}})}let[_]=await A.create(D).content({user_id:Z.id,ip_address:V,user_agent:G.headers.get("user-agent"),invalidated_at:null});if(!_)throw Error("Failed to create session");let H=Y(Z.id),z=Y(_.id),x=this.tokenManager.generateAccessToken({sub:H,sessionId:z,appMetadata:Z.app_metadata,userMetadata:Z.user_metadata}),M=this.tokenManager.generateRefreshToken(H,z);return await A.commit(),{user:f(Z),session:k(_),accessToken:x,refreshToken:M}}catch(Z){throw await A.cancel(),Z}}async startPasswordlessSignUp($,K){return this.otpManager.generate({user:{id:void 0,email:$.email,phone:$.phone},type:"signup",signupOptions:K})}async startPasswordlessSignIn($){let K=Y($.user_id),j=$.provider==="email"?$.provider_id:void 0,F=$.provider==="phone"?$.provider_id:void 0;return this.otpManager.generate({user:{id:K,email:j,phone:F},type:"signin"})}async completePasswordSignUp($,K,j,F,G,J){let V=await X.hash(F,10),A=await this.config.db.beginTransaction();try{let[Z]=await A.create(U).content({email:j.email??null,phone:j.phone??null,app_metadata:{...J?.appMetadata,roles:this.config.defaultSignupRoles},last_sign_in_at:N()});if(!Z)throw Error("Failed to create user");let[_]=await A.create(b).content({user_id:Z.id,provider:$,provider_id:K,identity_data:{password_hash:V}});if(!_)throw Error("Failed to create identity");let H=this.sessionManager.getClientIP(G),[z]=await A.create(D).content({user_id:Z.id,ip_address:H,user_agent:G.headers.get("user-agent"),invalidated_at:null});if(!z)throw Error("Failed to create session");let x=Y(Z.id),M=Y(z.id),B=this.tokenManager.generateAccessToken({sub:x,sessionId:M,appMetadata:Z.app_metadata,userMetadata:Z.user_metadata}),l=this.tokenManager.generateRefreshToken(x,M);return await A.commit(),{user:f(Z),session:k(z),accessToken:B,refreshToken:l}}catch(Z){throw await A.cancel(),Z}}async completePasswordSignIn($,K,j){let{password_hash:F}=$.identity_data;if(!F)throw Q.conflict("This account has no password. Use OTP to sign in.","NO_PASSWORD_SET");if(!await X.compare(K,F))throw Q.conflict("Invalid credentials","INVALID_CREDENTIALS");let J=Y($.user_id),V=await this.userManager.findById(J);if(!V)throw Q.notFound("User not found","USER_NOT_FOUND");return this.createAuthSession(V,j)}async completePasswordlessSignUp($,K){let j=$.email?"email":"phone",F=$.email??$.phone,G=$.signup_options,J=await this.config.db.beginTransaction();try{let[V]=await J.create(U).content({email:$.email??null,phone:$.phone??null,app_metadata:{...G?.appMetadata,roles:this.config.defaultSignupRoles},last_sign_in_at:N()});if(!V)throw Error("Failed to create user");let[A]=await J.create(b).content({user_id:V.id,provider:j,provider_id:F,identity_data:{}});if(!A)throw Error("Failed to create identity");let Z=this.sessionManager.getClientIP(K),[_]=await J.create(D).content({user_id:V.id,ip_address:Z,user_agent:K.headers.get("user-agent"),invalidated_at:null});if(!_)throw Error("Failed to create session");let H=Y(V.id),z=Y(_.id),x=this.tokenManager.generateAccessToken({sub:H,sessionId:z,appMetadata:V.app_metadata,userMetadata:V.user_metadata}),M=this.tokenManager.generateRefreshToken(H,z);return await J.commit(),{user:f(V),session:k(_),accessToken:x,refreshToken:M}}catch(V){throw await J.cancel(),V}}async completePasswordlessSignIn($,K){if(!$.user_id)throw Q.internalServerError("Invalid OTP record","INVALID_OTP_RECORD");let j=Y($.user_id),F=await this.userManager.findById(j);if(!F)throw Q.notFound("User not found","USER_NOT_FOUND");return this.createAuthSession(F,K)}async createAuthSession($,K){let j=await this.config.db.beginTransaction();try{let F=this.sessionManager.getClientIP(K),[G]=await j.create(D).content({user_id:$.id,ip_address:F,user_agent:K.headers.get("user-agent"),invalidated_at:null});if(!G)throw Error("Failed to create session");await j.update($.id).merge({last_sign_in_at:N()});let J=Y($.id),V=Y(G.id),A=this.tokenManager.generateAccessToken({sub:J,sessionId:V,appMetadata:$.app_metadata,userMetadata:$.user_metadata}),Z=this.tokenManager.generateRefreshToken(J,V);return await j.commit(),{user:f($),session:k(G),accessToken:A,refreshToken:Z}}catch(F){throw await j.cancel(),F}}validateSignUpMethod($){if($.type==="email")this.validateEmail($.email);else $.phone=this.normalizePhoneNumber($.phone)}validateSignInMethod($){if($.type==="email")this.validateEmail($.email);else $.phone=this.normalizePhoneNumber($.phone)}validateEmail($){if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test($))throw Q.badRequest("Invalid email format","INVALID_EMAIL")}normalizePhoneNumber($){let K=$.replace(/\D/g,"");if(K.length===10)return`+1${K}`;if(K.length===11&&K.startsWith("1"))return`+${K}`;if(!K.startsWith("+"))return`+${K}`;return K}extractContact($){return $.type==="email"?{email:$.email}:{phone:$.phone}}extractIdentityKey($){return $.type==="email"?{provider:"email",providerId:$.email}:{provider:"phone",providerId:$.phone}}initializeCleanup(){Promise.all([this.sessionManager.cleanup(),this.otpManager.cleanup()]).catch(()=>{}),c.schedule("0 2 * * *",async()=>{await Promise.all([this.sessionManager.cleanup(),this.otpManager.cleanup()]).catch(()=>{})})}}function Q$($){return new p($)}export{o as setAuthCookies,T as parseCookies,Q$ as createAuth,r as clearAuthCookies,O as COOKIE_NAMES,p as AuthService};
3
6
 
4
- //# debugId=16FC8A4D37BA05A064756E2164756E21
7
+ //# debugId=C5B001C14A95308E64756E2164756E21
package/dist/index.js CHANGED
@@ -1,6 +1,9 @@
1
1
  // @bun
2
- import{z as U}from"zod";class b{middlewares=[];constructor($=[]){this.middlewares=$}use($){return new b([...this.middlewares,$])}input($){return new O(this.middlewares,$)}query($){return{_input:U.object({}),_output:null,_ctx:null,_type:"query",handler:$,middlewares:this.middlewares}}mutation($){return{_input:U.object({}),_output:null,_ctx:null,_type:"mutation",handler:$,middlewares:this.middlewares}}formMutation($){return{_input:U.object({}),_output:null,_ctx:null,_type:"formMutation",handler:$,middlewares:this.middlewares}}subscription($){return{_input:U.object({}),_output:null,_ctx:null,_type:"subscription",handler:$,middlewares:this.middlewares}}file($){return{_input:U.object({}),_output:null,_ctx:null,_type:"file",handler:$,middlewares:this.middlewares}}fileStream($){return{_input:U.object({}),_output:null,_ctx:null,_type:"fileStream",handler:$,middlewares:this.middlewares}}html($){return{_input:U.object({}),_output:null,_ctx:null,_type:"html",handler:$,middlewares:this.middlewares}}}class O{middlewares;inputSchema;timeoutMs;constructor($,F,J){this.middlewares=$;this.inputSchema=F;this.timeoutMs=J}timeout($){return new O(this.middlewares,this.inputSchema,$)}query($){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"query",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}mutation($){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"mutation",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}formMutation($){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"formMutation",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}subscription($){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"subscription",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}file($){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"file",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}fileStream($){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"fileStream",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}html($){return{_input:U.object({}),_output:null,_ctx:null,_type:"html",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}}function g($){return new b}import{ZodError as y}from"zod";class I extends Error{code;clientCode;httpStatus;data;cause;static STATUS_MAP={BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_SUPPORTED:405,TIMEOUT:408,CONFLICT:409,PRECONDITION_FAILED:412,PAYLOAD_TOO_LARGE:413,UNPROCESSABLE_CONTENT:422,TOO_MANY_REQUESTS:429,CLIENT_CLOSED_REQUEST:499,INTERNAL_SERVER_ERROR:500,NOT_IMPLEMENTED:501,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504};constructor($){super($.message);if(this.name="BRPCError",this.code=$.code,this.clientCode=$.clientCode,this.httpStatus=I.STATUS_MAP[$.code],this.data=$.data,this.cause=$.cause,Error.captureStackTrace)Error.captureStackTrace(this,I)}toJSON(){return{name:this.name,code:this.code,clientCode:this.clientCode,message:this.message,data:this.data,httpStatus:this.httpStatus}}static badRequest($,F,J){return new I({code:"BAD_REQUEST",message:$,clientCode:F,data:J})}static unauthorized($="Unauthorized",F,J){return new I({code:"UNAUTHORIZED",message:$,clientCode:F,data:J})}static forbidden($="Forbidden",F,J){return new I({code:"FORBIDDEN",message:$,clientCode:F,data:J})}static notFound($="Not Found",F,J){return new I({code:"NOT_FOUND",message:$,clientCode:F,data:J})}static preconditionFailed($="Precondition failed",F,J){return new I({code:"NOT_FOUND",message:$,clientCode:F,data:J})}static conflict($,F,J){return new I({code:"CONFLICT",message:$,clientCode:F,data:J})}static unprocessableContent($,F,J){return new I({code:"UNPROCESSABLE_CONTENT",message:$,clientCode:F,data:J})}static tooManyRequests($="Too many requests",F,J){return new I({code:"TOO_MANY_REQUESTS",message:$,clientCode:F,data:J})}static internalServerError($="Internal Server Error",F,J){return new I({code:"INTERNAL_SERVER_ERROR",message:$,clientCode:F,data:J})}static timeout($="Request timeout",F,J){return new I({code:"TIMEOUT",message:$,clientCode:F,data:J})}}class Q{routeTree;routeCache;options;routeStats;constructor($,F={}){if(this.options={enableCaching:!0,maxCacheSize:1000,debug:!1,...F},this.routeCache=new Map,this.routeStats=this.analyzeRoutes($),this.routeTree=this.compileRoutes($),this.options.debug)console.log("[ROUTEMATCHER]","initialized with stats:",this.routeStats)}match($){if(this.options.enableCaching)return this.matchWithCache($);return this.matchDirect($)}matchWithCache($){let F=$.join("/");if(this.routeCache.has(F)){let _=this.routeCache.get(F);if(this.options.debug&&_)console.log("[ROUTEMATCHER]",`\uD83C\uDFAF Cache hit for route: /${F}`);return _}let J=this.matchDirect($);if(this.cacheResult(F,J),this.options.debug)console.log("[ROUTEMATCHER]",`\uD83D\uDCBE Cached route: /${F} -> ${J?"found":"not found"}`);return J}matchDirect($){let F=this.options.debug?performance.now():0,J=this.traverseTree(this.routeTree,$,0,{});if(this.options.debug){let _=performance.now()-F,W=J?.route._type||"none";console.log("[ROUTEMATCHER]",`\u26A1 Route match: /${$.join("/")} (${W}) took ${_.toFixed(3)}ms`)}return J}traverseTree($,F,J,_){if(J>=F.length){if($.handler)return{params:_,route:$.handler.handler};if($.index)return{params:_,route:$.index.handler};//! This was the last change I made trying to fix
2
+ import{z as U}from"zod";class O{middlewares=[];constructor($=[]){this.middlewares=$}use($){return new O([...this.middlewares,$])}input($){return new b(this.middlewares,$)}query($){return{_input:U.object({}),_output:null,_ctx:null,_type:"query",handler:$,middlewares:this.middlewares}}mutation($){return{_input:U.object({}),_output:null,_ctx:null,_type:"mutation",handler:$,middlewares:this.middlewares}}formMutation($){return{_input:U.object({}),_output:null,_ctx:null,_type:"formMutation",handler:$,middlewares:this.middlewares}}subscription($){return{_input:U.object({}),_output:null,_ctx:null,_type:"subscription",handler:$,middlewares:this.middlewares}}file($){return{_input:U.object({}),_output:null,_ctx:null,_type:"file",handler:$,middlewares:this.middlewares}}fileStream($){return{_input:U.object({}),_output:null,_ctx:null,_type:"fileStream",handler:$,middlewares:this.middlewares}}html($){return{_input:U.object({}),_output:null,_ctx:null,_type:"html",handler:$,middlewares:this.middlewares}}}class b{middlewares;inputSchema;timeoutMs;constructor($,F,J){this.middlewares=$;this.inputSchema=F;this.timeoutMs=J}timeout($){return new b(this.middlewares,this.inputSchema,$)}query($){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"query",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}mutation($){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"mutation",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}formMutation($){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"formMutation",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}subscription($){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"subscription",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}file($){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"file",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}fileStream($){return{_input:this.inputSchema,_output:null,_ctx:null,_type:"fileStream",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}html($){return{_input:U.object({}),_output:null,_ctx:null,_type:"html",_timeout:this.timeoutMs,handler:$,middlewares:this.middlewares}}}function h($){return new O}import{ZodError as y}from"zod";class I extends Error{code;clientCode;httpStatus;data;cause;static STATUS_MAP={BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_SUPPORTED:405,TIMEOUT:408,CONFLICT:409,PRECONDITION_FAILED:412,PAYLOAD_TOO_LARGE:413,UNPROCESSABLE_CONTENT:422,TOO_MANY_REQUESTS:429,CLIENT_CLOSED_REQUEST:499,INTERNAL_SERVER_ERROR:500,NOT_IMPLEMENTED:501,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504};constructor($){super($.message);if(this.name="BRPCError",this.code=$.code,this.clientCode=$.clientCode,this.httpStatus=I.STATUS_MAP[$.code],this.data=$.data,this.cause=$.cause,Error.captureStackTrace)Error.captureStackTrace(this,I)}toJSON(){return{name:this.name,code:this.code,clientCode:this.clientCode,message:this.message,data:this.data,httpStatus:this.httpStatus}}static badRequest($,F,J){return new I({code:"BAD_REQUEST",message:$,clientCode:F,data:J})}static unauthorized($="Unauthorized",F,J){return new I({code:"UNAUTHORIZED",message:$,clientCode:F,data:J})}static forbidden($="Forbidden",F,J){return new I({code:"FORBIDDEN",message:$,clientCode:F,data:J})}static notFound($="Not Found",F,J){return new I({code:"NOT_FOUND",message:$,clientCode:F,data:J})}static preconditionFailed($="Precondition failed",F,J){//! Intentionally maps to NOT_FOUND instead of PRECONDITION_FAILED — this was changed to fix a bug
3
+ //! but the exact cause is unknown. Suspected: some client/proxy/middleware misbehaved on 412
4
+ //! (not a browser auto-retry issue — 412 doesn't trigger those). Investigate before changing back.
5
+ return new I({code:"NOT_FOUND",message:$,clientCode:F,data:J})}static conflict($,F,J){return new I({code:"CONFLICT",message:$,clientCode:F,data:J})}static unprocessableContent($,F,J){return new I({code:"UNPROCESSABLE_CONTENT",message:$,clientCode:F,data:J})}static tooManyRequests($="Too many requests",F,J){return new I({code:"TOO_MANY_REQUESTS",message:$,clientCode:F,data:J})}static internalServerError($="Internal Server Error",F,J){return new I({code:"INTERNAL_SERVER_ERROR",message:$,clientCode:F,data:J})}static timeout($="Request timeout",F,J){return new I({code:"TIMEOUT",message:$,clientCode:F,data:J})}}class Q{routeTree;routeCache;options;routeStats;constructor($,F={}){if(this.options={enableCaching:!0,maxCacheSize:1000,debug:!1,...F},this.routeCache=new Map,this.routeStats=this.analyzeRoutes($),this.routeTree=this.compileRoutes($),this.options.debug)console.log("[ROUTEMATCHER]","initialized with stats:",this.routeStats)}match($){if(this.options.enableCaching)return this.matchWithCache($);return this.matchDirect($)}matchWithCache($){let F=$.join("/");if(this.routeCache.has(F)){let _=this.routeCache.get(F);if(this.options.debug&&_)console.log("[ROUTEMATCHER]",`\uD83C\uDFAF Cache hit for route: /${F}`);return _}let J=this.matchDirect($);if(this.cacheResult(F,J),this.options.debug)console.log("[ROUTEMATCHER]",`\uD83D\uDCBE Cached route: /${F} -> ${J?"found":"not found"}`);return J}matchDirect($){let F=this.options.debug?performance.now():0,J=this.traverseTree(this.routeTree,$,0,{});if(this.options.debug){let _=performance.now()-F,W=J?.route._type||"none";console.log("[ROUTEMATCHER]",`\u26A1 Route match: /${$.join("/")} (${W}) took ${_.toFixed(3)}ms`)}return J}traverseTree($,F,J,_){if(J>=F.length){if($.handler)return{params:_,route:$.handler.handler};if($.index)return{params:_,route:$.index.handler};//! This was the last change I made trying to fix
3
6
  //! Wildacrd not matching root (/) when index wasn't defined
4
- if($.wildcard&&F.length===0)return{params:{..._,wildcardPath:""},route:$.wildcard.handler};return null}let W=F[J],X=decodeURIComponent(W),Y=$.static.get(X);if(Y){let j=this.traverseTree(Y,F,J+1,_);if(j)return j}if($.param){let j={..._,[$.param.name]:X},Z=this.traverseTree($.param.node,F,J+1,j);if(Z)return Z}if($.wildcard){let j=F.slice(J).join("/");return{params:{..._,wildcardPath:j},route:$.wildcard.handler}}return null}compileRoutes($,F=[]){let J={static:new Map};for(let[_,W]of Object.entries($)){if(_==="index"&&this.isProcedure(W)){J.index={handler:W,paramNames:[...F],hasWildcard:!1};continue}if(_==="*"){if(this.isProcedure(W))J.wildcard={handler:W,paramNames:[...F,"wildcardPath"],hasWildcard:!0,wildcardIndex:F.length};continue}if(this.isProcedure(W)){let X={handler:W,paramNames:[...F],hasWildcard:!1};if(_.startsWith(":")){let Y=_.slice(1),j={static:new Map,handler:X};J.param={name:Y,node:j}}else{let Y={static:new Map,handler:X};J.static.set(_,Y)}}else if(typeof W==="object"&&W!==null)if(_.startsWith(":")){let X=_.slice(1),Y=this.compileRoutes(W,[...F,X]);J.param={name:X,node:Y}}else{let X=this.compileRoutes(W,F);J.static.set(_,X)}}return J}isProcedure($){return $&&typeof $==="object"&&"_type"in $&&"_input"in $}cacheResult($,F){if(this.routeCache.size>=this.options.maxCacheSize){let J=this.routeCache.keys().next().value;if(J!==void 0){if(this.routeCache.delete(J),this.options.debug)console.log(`\uD83D\uDDD1\uFE0F Evicted oldest cache entry: ${J}`)}}this.routeCache.set($,F)}analyzeRoutes($){let F={totalRoutes:0,staticRoutes:0,paramRoutes:0,wildcardRoutes:0,indexRoutes:0,maxDepth:0,procedureTypes:{query:0,mutation:0,subscription:0,file:0,fileStream:0,streamQuery:0,streamMutation:0,formMutation:0,html:0}},J=(_,W=0)=>{F.maxDepth=Math.max(F.maxDepth,W);for(let[X,Y]of Object.entries(_))if(this.isProcedure(Y)){if(F.totalRoutes++,Y._type in F.procedureTypes)F.procedureTypes[Y._type]++;if(X==="index")F.indexRoutes++;else if(X.startsWith(":"))F.paramRoutes++;else if(X==="*")F.wildcardRoutes++;else F.staticRoutes++}else if(typeof Y==="object"&&Y!==null)J(Y,W+1)};return J($),F}clearCache(){if(this.routeCache.clear(),this.options.debug)console.log("[ROUTEMATCHER]","\uD83E\uDDF9 Route cache cleared")}getCacheStats(){return{size:this.routeCache.size,maxSize:this.options.maxCacheSize,routeStats:this.routeStats}}getOptimizationSuggestions(){let $=[],{routeStats:F}=this;if(F.paramRoutes>F.staticRoutes*2)$.push("\uD83D\uDE80 Consider grouping routes to reduce parameter route overhead");if(F.maxDepth>6)$.push("\uD83D\uDCCA Deep route nesting detected - consider flattening some routes");if(F.totalRoutes>200)$.push("\u26A1 Large route table - consider route splitting or lazy loading");if(!this.options.enableCaching&&F.totalRoutes>50)$.push("\uD83D\uDCBE Enable caching for better performance with many routes");if(F.procedureTypes.file>20)$.push("\uD83D\uDCC1 Many file routes detected - consider static file serving");if(F.procedureTypes.subscription>10)$.push("\uD83D\uDD04 Many subscription routes - ensure WebSocket optimization");return $}findRoutesByPattern($){return[]}getRouteInfo($){let F=$.split("/").filter(Boolean),J=F.join("/"),_=this.routeCache.has(J),W=performance.now(),X=this.matchDirect(F),Y=performance.now()-W;return{found:!!X,route:X?.route,params:X?.params,cached:_,matchTime:Y}}benchmark($,F=1000){let J=$.map((G)=>G.split("/").filter(Boolean)),_=new Map(this.routeCache);this.routeCache.clear();let W=performance.now();for(let G=0;G<F;G++)for(let D of J)this.matchDirect(D);let Y=performance.now()-W,j=F*$.length,Z=Y/j,L=j/Y*1000;return this.routeCache=_,{averageTime:Z,totalTime:Y,routesPerSecond:L}}warmupCache($){if(!this.options.enableCaching)return;let F=performance.now(),J=0;for(let W of $){let X=W.split("/").filter(Boolean),Y=this.matchDirect(X);if(Y)this.cacheResult(X.join("/"),Y),J++}let _=performance.now();if(this.options.debug)console.log("[ROUTEMATCHER]",`\uD83D\uDD25 Cache warmed up: ${J} routes in ${(_-F).toFixed(2)}ms`)}}var C=import.meta.require,x={NO_CACHE:{cacheControl:"no-cache, no-store, must-revalidate",expires:new Date(0).toUTCString()},ONE_YEAR:{cacheControl:"public, max-age=31536000",expires:new Date(Date.now()+31536000000).toUTCString()},IMMUTABLE:{cacheControl:"public, max-age=31536000, immutable",expires:new Date(Date.now()+31536000000).toUTCString()},ONE_MONTH:{cacheControl:"public, max-age=2592000"},ONE_WEEK:{cacheControl:"public, max-age=604800"},ONE_DAY:{cacheControl:"public, max-age=86400, must-revalidate"},ONE_HOUR:{cacheControl:"public, max-age=3600, must-revalidate"},FIVE_MINUTES:{cacheControl:"public, max-age=300"},API:{cacheControl:"public, max-age=300, s-maxage=3600"},STYLESHEET:{cacheControl:"public, max-age=2592000, must-revalidate"},JAVASCRIPT:{cacheControl:"public, max-age=2592000, must-revalidate"},HASHED_ASSET:{cacheControl:"public, max-age=31536000, immutable",expires:new Date(Date.now()+31536000000).toUTCString()},FONT:{cacheControl:"public, max-age=31536000",expires:new Date(Date.now()+31536000000).toUTCString()},IMAGE:{cacheControl:"public, max-age=2592000"},FAVICON:{cacheControl:"public, max-age=31536000",expires:new Date(Date.now()+31536000000).toUTCString()},HTML_PAGE:{cacheControl:"public, max-age=3600, must-revalidate"},API_RESPONSE:{cacheControl:"public, max-age=300, s-maxage=900"},FEED:{cacheControl:"public, max-age=3600"},SITEMAP:{cacheControl:"public, max-age=86400"},MANIFEST:{cacheControl:"public, max-age=86400, must-revalidate"},SERVICE_WORKER:{cacheControl:"no-cache, no-store, must-revalidate",expires:new Date(0).toUTCString()},MEDIA_STREAM:{cacheControl:"no-cache, no-store"},DOCUMENT:{cacheControl:"public, max-age=86400"},ARCHIVE:{cacheControl:"public, max-age=604800"}};function q($){return x[$]}var v={html:"text/html; charset=utf-8",css:"text/css",javascript:"application/javascript",json:"application/json",xml:"application/xml",text:"text/plain; charset=utf-8",csv:"text/csv",markdown:"text/markdown; charset=utf-8",png:"image/png",jpeg:"image/jpeg",gif:"image/gif",svg:"image/svg+xml",webp:"image/webp",icon:"image/x-icon",bmp:"image/bmp",woff:"font/woff",woff2:"font/woff2",truetype:"font/ttf","embedded-opentype":"application/vnd.ms-fontobject",opentype:"font/otf",mp4:"video/mp4",webm:"video/webm",ogg:"video/ogg",mpeg:"audio/mpeg",wav:"audio/wav",flac:"audio/flac",pdf:"application/pdf",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",zip:"application/zip",tar:"application/x-tar",gzip:"application/gzip","7z":"application/x-7z-compressed",js:"application/javascript",txt:"text/plain; charset=utf-8",jpg:"image/jpeg",ico:"image/x-icon",ttf:"font/ttf",eot:"application/vnd.ms-fontobject",otf:"font/otf",mp3:"audio/mpeg",gz:"application/gzip"};function B($){return v[$]||"application/octet-stream"}function z($){if(typeof Bun<"u"&&Bun.hash)return Bun.hash($).toString(16);try{let F=C("crypto"),J;if(typeof $==="string")J=Buffer.from($);else if($ instanceof ArrayBuffer)J=Buffer.from(new Uint8Array($));else J=Buffer.from($);return F.createHash("md5").update(J).digest("hex")}catch{let F=typeof $==="string"?new TextEncoder().encode($):$ instanceof ArrayBuffer?new Uint8Array($):$,J=0;for(let _=0;_<F.length;_++)J=(J<<5)-J+F[_],J=J&J;return Math.abs(J).toString(16).padStart(16,"0")}}function V($,F,J){let _=$.headers.get("Origin"),W=!0;if((J?.allowAll??!0)&&_)return _;if(F.length===0)return _||"*";if(!_)return F[0];if(F.includes(_))return _;return F[0]}class S{headers={};contentType($){return this.headers["Content-Type"]=B($),this}filePath($){let F=$.split(".").pop()?.toLowerCase()||"";return this.headers["Content-Type"]=B(F),this}mimeType($){return this.headers["Content-Type"]=$,this}cache($){let{cacheControl:F,expires:J}=q($);if(this.headers["Cache-Control"]=F,J)this.headers.Expires=J;return this}eTag($){if(!$)return this;let F;if(typeof $==="string"&&$.startsWith('"'))F=$;else F=`"${z($)}"`;return this.headers.ETag=F,this}lastModified($){return this.headers["Last-Modified"]=$.toUTCString(),this}contentLength($){return this.headers["Content-Length"]=$.toString(),this}redirect($,F=!1){return this.headers.Location=$,this.headers["X-Redirect-Type"]=F?"permanent":"temporary",this}cors({origin:$="*",methods:F=["GET","POST","PUT","DELETE","OPTIONS"],headers:J=["Content-Type","Authorization"],maxAge:_=86400,credentials:W}={}){let X=W??$!=="*";if(X&&$==="*")throw Error('CORS: Cannot use credentials with wildcard origin "*". Specify an exact origin or set credentials to false.');if(this.headers["Access-Control-Allow-Origin"]=$,this.headers["Access-Control-Allow-Methods"]=F.join(", "),this.headers["Access-Control-Allow-Headers"]=J.join(", "),X)this.headers["Access-Control-Allow-Credentials"]="true";return this.headers["Access-Control-Max-Age"]=_.toString(),this}security($){let F={hsts:!0,noSniff:!0,frameOptions:"SAMEORIGIN",xssProtection:!0,...$};if(F.csp)this.headers["Content-Security-Policy"]=F.csp;if(F.hsts){let J=typeof F.hsts==="number"?F.hsts:31536000;this.headers["Strict-Transport-Security"]=`max-age=${J}; includeSubDomains`}if(F.noSniff)this.headers["X-Content-Type-Options"]="nosniff";if(F.frameOptions)this.headers["X-Frame-Options"]=F.frameOptions;if(F.xssProtection)this.headers["X-XSS-Protection"]="1; mode=block";return this}custom($,F){return this.headers[$]=F,this}customHeaders($){return Object.assign(this.headers,$),this}vary(...$){let F=this.headers.Vary,J=F?`${F}, ${$.join(", ")}`:$.join(", ");return this.headers.Vary=J,this}compress($){if($)this.headers["Content-Encoding"]=$;return this}build($){let F={...this.headers};if($&&!F["Content-Length"]){let J=typeof $==="string"?new TextEncoder().encode($).length:$.byteLength;F["Content-Length"]=J.toString()}return F}buildHeaders($){return new Headers(this.build($))}}function A(){return new S}function l($,F){let J=A().contentType($);if(F)J.cache(F);return J.build()}var c={css:($)=>A().contentType("css").cache("STYLESHEET").eTag($),javascript:($)=>A().contentType("javascript").cache("JAVASCRIPT").eTag($),hashedAsset:($,F)=>A().filePath($).cache("HASHED_ASSET").eTag(F),font:($)=>A().filePath($).cache("FONT").cors({origin:"*"}),image:($)=>A().filePath($).cache("IMAGE"),favicon:()=>A().contentType("icon").cache("FAVICON"),api:()=>A().contentType("json").cache("API_RESPONSE").cors(),expensiveApi:($)=>A().contentType("json").cache("API_RESPONSE").cors().eTag($),realtime:()=>A().contentType("json").cache("NO_CACHE").cors(),html:()=>A().contentType("html").cache("HTML_PAGE").security(),expensiveHtml:($)=>A().contentType("html").cache("HTML_PAGE").security().eTag($),manifest:($)=>A().contentType("json").cache("MANIFEST").eTag($),serviceWorker:()=>A().contentType("javascript").cache("SERVICE_WORKER"),sitemap:($)=>A().contentType("xml").cache("SITEMAP").eTag($),feed:($,F="rss")=>A().contentType("xml").cache("FEED").eTag($).mimeType(F==="rss"?"application/rss+xml":"application/atom+xml"),video:($)=>A().filePath($).cache("MEDIA_STREAM").custom("Accept-Ranges","bytes"),audio:($)=>A().filePath($).cache("MEDIA_STREAM").custom("Accept-Ranges","bytes"),pdf:($)=>A().contentType("pdf").cache("DOCUMENT").custom("Content-Disposition","inline").eTag($),download:($,F,J)=>A().filePath(F).cache("ARCHIVE").custom("Content-Disposition",`attachment; filename="${$}"`).eTag(J),secure:()=>A().security({csp:"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'",hsts:31536000}).custom("X-Powered-By","Bun"),permanentRedirect:($)=>A().redirect($,!0),temporaryRedirect:($)=>A().redirect($,!1)};async function T($,F,J,_={}){let{maxChunkSize:W=2097152,cacheMaxAge:X=3600,acceptedExtensions:Y}=_;if(!await $.exists())return new Response("Media file not found",{status:404,headers:A().contentType("text").cors({origin:V(F,J)}).build()});if(Y&&Y.length>0){let K=$.name?.toLowerCase().split(".").pop();if(!K||!Y.includes(K))return new Response("File type not supported",{status:415,headers:A().contentType("text").cors({origin:V(F,J)}).build()})}let Z=$.size,L=F.headers.get("Range");if(!L)return new Response($,{headers:A().filePath($.name||"media").custom("Accept-Ranges","bytes").custom("Cache-Control",`public, max-age=${X}`).cors({origin:V(F,J)}).build()});let G=L.match(/bytes=(\d*)-(\d*)/);if(!G)return new Response("Invalid Range header",{status:416,headers:A().contentType("text").cors({origin:V(F,J)}).custom("Content-Range",`bytes */${Z}`).build()});let D=G[1]?parseInt(G[1]):0,N=G[2]?parseInt(G[2]):Z-1,M=Math.min(N,D+W-1,Z-1);if(D>=Z||M>=Z||D>M)return new Response("Range not satisfiable",{status:416,headers:A().contentType("text").cors({origin:V(F,J)}).custom("Content-Range",`bytes */${Z}`).build()});return new Response($.slice(D,M+1),{status:206,headers:A().filePath($.name||"media").custom("Accept-Ranges","bytes").custom("Cache-Control",`public, max-age=${X}`).cors({origin:V(F,J)}).build()})}function f($,F,J){return{type:"stream",file:$,request:F,options:J}}class E{routes;routeMatcher;contextCreator;prefix;globalMiddlewares;server=null;wsConfig;integrations;development=!0;debug=!1;allowedOrigins=[];onError;activeConnections=new Set;maxConnections=parseInt(process.env.MAX_WS_CONNECTIONS??"1000");connectionTimeout=parseInt(process.env.WS_TIMEOUT??"30000");maxRequestSize=parseInt(process.env.MAX_REQUEST_SIZE??"10485760");requestTimeout=parseInt(process.env.REQUEST_TIMEOUT??"30000");constructor($){if(this.contextCreator=$.context,this.prefix=$.prefix??"",this.routes=$.routes,this.globalMiddlewares=$.globalMiddlewares??[],this.wsConfig=$.websocket??{},this.integrations=$.integrations,this.debug=!!$.debug&&!0,this.onError=$.onError,this.routeMatcher=new Q($.routes,{enableCaching:!this.development,maxCacheSize:parseInt(process.env.ROUTE_CACHE_SIZE??"1000"),debug:this.debug}),!this.development)setInterval(()=>this.cleanupStaleConnections(),60000);if(this.debug)console.log("Router initialized with route matcher"),console.log("Route cache stats:",this.routeMatcher.getCacheStats())}findRoute($){return this.routeMatcher.match($)}async parseInput($,F,J){let _={},W=new URL($.url),X=Object.fromEntries(W.searchParams),Y=$.method;if(Y==="POST"){if(parseInt($.headers.get("content-length")||"0")>this.maxRequestSize)throw Error("Request payload too large")}if(Y==="GET"&&["query","streamQuery","file","fileStream","html"].includes(J))_=X;else if(Y==="POST"&&["mutation","formMutation","streamMutation"].includes(J)){let j=$.headers.get("content-type");if(j?.includes("application/json"))_=await $.json();else if(j?.includes("multipart/form-data")){let Z=await $.formData();_=await this.parseFormData(Z)}else throw Error("Unsupported content type")}else throw Error(`Unsupported request method ${Y} for procedure type ${J}`);return F.parse(_)}async parseFormData($){let F={},J={};$.forEach(async(_,W)=>{if(_ instanceof File){if(!J[W])J[W]=[];J[W].push(_)}else if(typeof _==="string")try{F[W]=JSON.parse(_)}catch{F[W]=_}});for(let[_,W]of Object.entries(J))F[_]=W;return F}handleError($,F){if(this.onError)try{this.onError($,{req:F,route:F.url})}catch(J){console.error("Error logger failed:",J)}if($?.errors&&Array.isArray($.errors)&&$.name==="ZodError")return new Response(JSON.stringify({error:{name:"ValidationError",code:"BAD_REQUEST",clientCode:"VALIDATION_ERROR",message:"Input validation failed",data:{validationErrors:$.errors.map((J)=>({path:J.path.join("."),message:J.message,code:J.code,received:J.received}))},httpStatus:400}}),{status:400,headers:A().contentType("json").cors({origin:V(F,this.allowedOrigins)}).build()});if($ instanceof I)return new Response(JSON.stringify({error:$.toJSON()}),{status:$.httpStatus,headers:A().contentType("json").cors({origin:V(F,this.allowedOrigins)}).build()});if($ instanceof Error){let J=$.message==="Not Found"?404:$.message==="Unauthorized"?401:$.message==="Forbidden"?403:$.message==="Request payload too large"?413:$.message==="Request timeout"?504:500,_=J===404?"NOT_FOUND":J===401?"UNAUTHORIZED":J===403?"FORBIDDEN":J===413?"PAYLOAD_TOO_LARGE":J===504?"GATEWAY_TIMEOUT":"INTERNAL_SERVER_ERROR",W=J<500?$.message:"Internal Server Error";return new Response(JSON.stringify({error:{code:_,message:W}}),{status:J,headers:A().contentType("json").cors({origin:V(F,this.allowedOrigins)}).build()})}return new Response(JSON.stringify({error:{code:"INTERNAL_SERVER_ERROR",message:"Internal Server Error"}}),{status:500,headers:A().contentType("json").cors({origin:V(F,this.allowedOrigins)}).build()})}handleFileCacheControl($){let F;if($?.includes("text/html"))F="public, max-age=0, must-revalidate";else if($?.includes("text/css")||$?.includes("text/javascript")||$?.includes("application/javascript"))F="public, max-age=31536000, immutable";else F="public, max-age=86400, stale-while-revalidate=604800";return F}async handleRequest($,F){try{if($.method==="OPTIONS")return new Response(null,{status:204,headers:A().cors({origin:V($,this.allowedOrigins)}).build()});let _=new URL($.url).pathname;if(_=_.startsWith(this.prefix)?_.slice(this.prefix.length):_,_=_.startsWith("/")?_:"/"+_,_==="/ws"&&F){if(this.debug)console.log("\uD83D\uDD0C WebSocket upgrade requested");return this.handleWebSocketUpgrade($,F)}let W=_==="/"?[]:_.split("/").filter(Boolean),X=this.findRoute(W);if(!X)return new Response("Not Found",{status:404,headers:A().contentType("text").cors({origin:V($,this.allowedOrigins)}).build()});let{route:Y}=X,j=Y._timeout??this.requestTimeout,Z=new Promise((L,G)=>{setTimeout(()=>G(Error("Request timeout")),j)});return await Promise.race([this._handleRequest($,F,X),Z])}catch(J){return this.handleError(J,$)}}async _handleRequest($,F,J){try{if(!J)return new Response("Not Found",{status:404,headers:A().contentType("text").cors({origin:V($,this.allowedOrigins)}).build()});let{params:_,route:W}=J;if(!W.handler)return new Response("Handler not implemented",{status:501,headers:A().contentType("text").cors({origin:V($,this.allowedOrigins)}).build()});let X=new Headers,Y=await this.contextCreator($);Object.assign(Y,{req:$,params:_,publishToProcedure:async(G,D,N)=>{await this.publishToProcedure(G,D,Y,N)},headers:X});for(let G of this.globalMiddlewares){let D=await G(Y);if(D!==null&&D!==void 0&&typeof D==="object")Object.assign(Y,D)}for(let G of W.middlewares){let D=await G(Y);if(D!==null&&D!==void 0&&typeof D==="object")Object.assign(Y,D)}let j=await this.parseInput($,W._input,W._type),Z=await W.handler({ctx:Y,input:j}),L=A().cors({origin:V($,this.allowedOrigins)}).build();return Object.keys(L).forEach((G)=>{X.set(G,L[G])}),await this.handleProcedureResponse(W._type,Z,X)}catch(_){return this.handleError(_,$)}}async testRequest($){return this.handleRequest($)}async handleProcedureResponse($,F,J){switch($){case"file":if(F instanceof Response)return F;let _=F.type;return J.append("Content-Type",_),J.append("Cache-Control",this.development?"public, max-age=0, must-revalidate":this.handleFileCacheControl(_)),new Response(F,{headers:J});case"fileStream":if(typeof F==="object"&&F!==null&&"type"in F&&F.type==="stream"){let X=F;return await T(F.file,F.request,this.allowedOrigins,X.options)}let W=F.type;return J.append("Content-Type",W),J.append("Cache-Control",this.development?"public, max-age=0, must-revalidate":this.handleFileCacheControl(W)),new Response(F,{headers:J});case"html":if(typeof F==="string")return J.append("Content-Type","text/html"),new Response(F,{headers:J});break;case"query":case"mutation":case"formMutation":case"streamQuery":case"streamMutation":case"subscription":default:return J.append("Content-Type","application/json"),new Response(JSON.stringify({data:F}),{headers:J})}return J.append("Content-Type","application/json"),new Response(JSON.stringify({data:F}),{headers:J})}cleanupStaleConnections(){let $=Date.now();for(let F of this.activeConnections)if($-F.data.lastActivity>this.connectionTimeout)F.close(),this.activeConnections.delete(F)}async handleWebSocketUpgrade($,F){if(this.activeConnections.size>=this.maxConnections)return new Response("Too many connections",{status:503});let J=await this.contextCreator($);Object.assign(J,{req:$});for(let W of this.globalMiddlewares){let X=await W(J);if(X!==null&&X!==void 0&&typeof X==="object")Object.assign(J,X)}if(F.upgrade($,{data:{ctx:J,topics:new Set,lastActivity:Date.now()}}))return new Response(null,{status:101});return new Response("WebSocket upgrade failed",{status:500})}async handleAuthenticate($,F,J){try{let _=F.startsWith("Bearer ")?F.substring(7):F;if($.data.ctx.authToken=_,$.send(JSON.stringify({type:"auth_success",message:"Authentication successful"})),this.debug)console.log("[BRPC]","WebSocket authenticated successfully")}catch(_){if($.send(JSON.stringify({type:"auth_error",error:_ instanceof Error?_.message:"Authentication failed"})),this.debug)console.error("WebSocket authentication error:",_)}}async handleSubscribe($,F,J){let _=this.findRoute(F.split("/").filter(Boolean));if(!_||_.route._type!=="subscription")throw Error("Invalid subscription route");$.subscribe(F),$.data.topics.add(F)}async handleUnsubscribe($,F,J){let _=this.findRoute(F.split("/").filter(Boolean));if(!_||_.route._type!=="subscription")throw Error("Invalid subscription route");$.unsubscribe(F),$.data.topics.delete(F)}async handlePublish($,F,J,_){let W=this.findRoute(F.split("/").filter(Boolean));if(!W)throw Error("Subscription not found");let{params:X,route:Y}=W;if(Y._type!=="subscription"||!Y.handler)throw Error("Invalid subscription route");Object.assign(_,{params:X});let j=Y._input.fields?Y._input.parse(J):J,Z=await Y.handler({ctx:_,input:j}),L={topic:F,data:Z,type:"publish"};if(this.debug)console.log(`Server will publish to ${F}`,L);this.server.publish(F,JSON.stringify(L))}async publishToProcedure($,F,J,_){if($._type!=="subscription"||!$.handler)throw Error("Can only publish to subscription procedures");let W=this.findRoutePath(this.routes,$);if(!W)throw Error("Subscription route not found");let X=W.split("/").map((Z)=>{if(Z.startsWith(":")){let L=Z.slice(1),G=_[L];if(!G)throw Error(`Missing parameter: ${L}`);return G}return Z}).join("/"),Y=await $.handler({ctx:{...J,params:_},input:F}),j={topic:X,data:Y,type:"publish"};if(this.debug)console.log(`Server will publish to ${X}`,j);return this.server?.publish(X,JSON.stringify(j)),Y}async publish($,F,J){if($._type!=="subscription"||!$.handler)throw Error("Can only publish to subscription procedures");let _=this.findRoutePath(this.routes,$);if(!_)throw Error("Subscription route not found");let W=_.split("/").map((j)=>{if(j.startsWith(":")){let Z=j.slice(1),L=J?J[Z]:null;if(!L)throw Error(`Missing parameter: ${Z}`);return L}return j}).join("/"),X=await $.handler({ctx:{params:J},input:F}),Y={topic:W,data:X,type:"publish"};if(this.debug)console.log(`Server will publish to ${W}`,Y);return this.server?.publish(W,JSON.stringify(Y)),X}freePublish($,F){let J={topic:$,data:F,type:"publish"};this.server?.publish($,JSON.stringify(J))}findRoutePath($,F,J=""){for(let[_,W]of Object.entries($)){let X=J?`${J}/${_}`:_;if(W===F)return X;if(W&&typeof W==="object"&&!("_type"in W)){let Y=this.findRoutePath(W,F,X);if(Y)return Y}}return null}getIntegrationRoutes(){if(!this.integrations)return;let $={};if(this.integrations.betterAuth){let F=this.integrations.betterAuth.handler;if(!F)throw Error("Please provide a handler for betterAuth integration");$["/api/auth/*"]=async(J)=>{return await F(J)}}if(this.integrations.rawRoutes)Object.keys(this.integrations.rawRoutes).forEach((F)=>{$[F]=this.integrations.rawRoutes[F]});return Object.keys($).length>0?$:void 0}getRouteStats(){return{cache:this.routeMatcher.getCacheStats(),suggestions:this.routeMatcher.getOptimizationSuggestions()}}clearRouteCache(){if(this.routeMatcher.clearCache(),this.debug)console.log("[ROUTEMATCHER] Route cache cleared")}benchmarkRoutes($=["/users/123","/posts/456/comments"],F=1000){if(this.debug){let J=this.routeMatcher.benchmark($,F);return console.log("[ROUTEMATCHER]","Route matching benchmark:",J),J}}updateRoutes($){if(this.development)this.routes=$,this.routeMatcher=new Q($,{enableCaching:!1,debug:this.debug}),console.log("[HMR]","Server Routes updated");else console.warn("Route updates are only allowed in development mode")}get url(){return this.server?.url.toString()??null}listen($,F){if(this.server=Bun.serve({port:$,routes:this.getIntegrationRoutes(),fetch:(J,_)=>this.handleRequest(J,_),websocket:{open:(J)=>{if(this.activeConnections.add(J),this.wsConfig?.onOpen)this.wsConfig.onOpen(J,J.data.ctx);if(this.debug)console.log("[BRPC]","WebSocket connection opened",{connections:this.activeConnections.size})},message:async(J,_)=>{try{J.data.lastActivity=Date.now();let{type:W,topic:X,token:Y,data:j}=JSON.parse(_),Z=J.data.ctx;if(this.debug)console.log("[BRPC]","WebSocket message received:",{type:W,topic:X});switch(W){case"authenticate":await this.handleAuthenticate(J,Y,Z);break;case"subscribe":await this.handleSubscribe(J,X,Z);break;case"unsubscribe":await this.handleUnsubscribe(J,X,Z);break;case"publish":await this.handlePublish(J,X,j,Z);break;default:if(J.send(JSON.stringify({error:"Unknown message type"})),this.debug)console.error("Unknown WebSocket message type:",W)}}catch(W){if(this.debug)console.error("[BRPC]","WebSocket message error:",W);if(W instanceof y)J.send(JSON.stringify({error:"Invalid message format"}));else if(W instanceof Error)J.send(JSON.stringify({error:W.message}))}},close:(J,_,W)=>{if(this.activeConnections.delete(J),J.data.topics.forEach((X)=>{J.unsubscribe(X)}),this.debug)console.log("[BRPC]","WebSocket connection closed",{code:_,reason:W,remainingConnections:this.activeConnections.size});if(this.wsConfig?.onClose)this.wsConfig.onClose(J,_,W,J.data.ctx)}}}),F)F()}}function e($){return new E($)}function F$($){return $}import{z as P}from"zod";var w={image:["image/jpeg","image/png","image/gif","image/webp","image/svg+xml"],video:["video/mp4","video/webm","video/ogg"],audio:["audio/mpeg","audio/ogg","audio/wav"],document:["application/pdf","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]},H=($,F="MB")=>{switch(F){case"MB":return $*1024*1024;case"KB":return $*1024;case"B":return $}},W$=({acceptedTypes:$,maxSize:F=1/0,minSize:J,messages:_={}}={})=>{let W=F?H(F):1/0,X=J?H(J):0,Y=$?Object.entries($).flatMap(([Z,L])=>{let G=Z;if(L==="*")return w[G];if(Array.isArray(L))return L;return[]}):void 0,j=P.instanceof(File,{message:_.required??"File is required"});if(j=j.refine((Z)=>Z.size<=W,_.maxSize??`File size must be less than ${F}MB`).refine((Z)=>Z.size>=X,_.minSize??`File size must be at least ${J}MB`),Y?.length)j=j.refine((Z)=>Y.includes(Z.type),_.type??`File must be of type: ${Y.join(", ")}`);return j};var Y$=($)=>{let F=$.paths.map((_)=>new RegExp(_)),J="Not Found";return async(_)=>{let X=new URL(_.req.url).pathname;if(F.some((Y)=>Y.test(X)))throw Error("Not Found")}};class R{requests=new Map;lastCleanupTime=Date.now();alertThreshold;config;constructor($){this.config={windowMs:$.windowMs,maxRequests:$.maxRequests,maxEntries:$.maxEntries??1e4,cleanupIntervalMs:$.cleanupIntervalMs??60000,message:$.message??"Too Many Requests",statusCode:$.statusCode??429,headerPrefix:$.headerPrefix??"X-RateLimit"},this.alertThreshold=this.config.maxEntries*0.8}cleanup(){let $=Date.now(),F=0;for(let[J,_]of this.requests.entries()){let W=$-_.startTime>this.config.windowMs,X=$-_.lastAccessed>this.config.windowMs*2;if(W||X)this.requests.delete(J),F++}return this.lastCleanupTime=$,F}handleMaxEntries($){let F=this.requests.size;if(Date.now()-this.lastCleanupTime>this.config.cleanupIntervalMs)this.cleanup();if(F>=this.alertThreshold)console.warn(`Rate limiter at ${Math.round(F/this.config.maxEntries*100)}% capacity`);if(this.requests.has($))return!0;return F<this.config.maxEntries}check($){if(!this.handleMaxEntries($))return{isLimited:!0,remaining:0,resetTime:Date.now()+this.config.windowMs};let F=Date.now(),J=this.requests.get($);if(!J||F-J.startTime>=this.config.windowMs)return this.requests.set($,{count:1,startTime:F,lastAccessed:F}),{isLimited:!1,remaining:this.config.maxRequests-1,resetTime:F+this.config.windowMs};J.lastAccessed=F;let _=J.count>=this.config.maxRequests;if(!_)J.count++;return{isLimited:_,remaining:Math.max(0,this.config.maxRequests-J.count),resetTime:J.startTime+this.config.windowMs}}getConfig(){return this.config}}var A$=($)=>{let F=new R($);return async(J)=>{if(J.req.headers.get("range"))return;let _=J.req.headers.get("x-forwarded-for")?.split(",")[0]||J.req.headers.get("x-real-ip")||"unknown",{isLimited:W,remaining:X,resetTime:Y}=F.check(_),{headerPrefix:j}=F.getConfig();if(J.headers.set(`${j}-Remaining`,X.toString()),J.headers.set(`${j}-Reset`,Y.toString()),W)throw J.headers.set(`${j}-Exceeded`,"true"),Error(F.getConfig().message)}};function G$($,F){return F??$}async function I$($,F){let J=performance.now();try{return await F()}catch(_){let W=performance.now();throw console.log(`[PERF] ${$} failed after ${(W-J).toFixed(4)} ms`),_}finally{let _=performance.now();console.log(`[PERF] ${$}: ${(_-J).toFixed(4)} ms`)}}export{f as streamMedia,l as quickHeaders,I$ as measure,z as hashContent,B as getMimeType,V as getCorsOrigin,e as createRouter,A$ as createRateLimiter,g as createProcedure,Y$ as createPathBlocker,G$ as createMiddleware,W$ as createFileSchema,F$ as createContext,c as commonHeaders,A as buildHeaders,E as Router,I as BRPCError};
7
+ if($.wildcard&&F.length===0)return{params:{..._,wildcardPath:""},route:$.wildcard.handler};return null}let W=F[J],X=decodeURIComponent(W),Y=$.static.get(X);if(Y){let j=this.traverseTree(Y,F,J+1,_);if(j)return j}if($.param){let j={..._,[$.param.name]:X},Z=this.traverseTree($.param.node,F,J+1,j);if(Z)return Z}if($.wildcard){let j=F.slice(J).join("/");return{params:{..._,wildcardPath:j},route:$.wildcard.handler}}return null}compileRoutes($,F=[]){let J={static:new Map};for(let[_,W]of Object.entries($)){if(_==="index"&&this.isProcedure(W)){J.index={handler:W,paramNames:[...F],hasWildcard:!1};continue}if(_==="*"){if(this.isProcedure(W))J.wildcard={handler:W,paramNames:[...F,"wildcardPath"],hasWildcard:!0,wildcardIndex:F.length};continue}if(this.isProcedure(W)){let X={handler:W,paramNames:[...F],hasWildcard:!1};if(_.startsWith(":")){let Y=_.slice(1),j={static:new Map,handler:X};J.param={name:Y,node:j}}else{let Y={static:new Map,handler:X};J.static.set(_,Y)}}else if(typeof W==="object"&&W!==null)if(_.startsWith(":")){let X=_.slice(1),Y=this.compileRoutes(W,[...F,X]);J.param={name:X,node:Y}}else{let X=this.compileRoutes(W,F);J.static.set(_,X)}}return J}isProcedure($){return $&&typeof $==="object"&&"_type"in $&&"_input"in $}cacheResult($,F){if(this.routeCache.size>=this.options.maxCacheSize){let J=this.routeCache.keys().next().value;if(J!==void 0){if(this.routeCache.delete(J),this.options.debug)console.log(`\uD83D\uDDD1\uFE0F Evicted oldest cache entry: ${J}`)}}this.routeCache.set($,F)}analyzeRoutes($){let F={totalRoutes:0,staticRoutes:0,paramRoutes:0,wildcardRoutes:0,indexRoutes:0,maxDepth:0,procedureTypes:{query:0,mutation:0,subscription:0,file:0,fileStream:0,streamQuery:0,streamMutation:0,formMutation:0,html:0}},J=(_,W=0)=>{F.maxDepth=Math.max(F.maxDepth,W);for(let[X,Y]of Object.entries(_))if(this.isProcedure(Y)){if(F.totalRoutes++,Y._type in F.procedureTypes)F.procedureTypes[Y._type]++;if(X==="index")F.indexRoutes++;else if(X.startsWith(":"))F.paramRoutes++;else if(X==="*")F.wildcardRoutes++;else F.staticRoutes++}else if(typeof Y==="object"&&Y!==null)J(Y,W+1)};return J($),F}clearCache(){if(this.routeCache.clear(),this.options.debug)console.log("[ROUTEMATCHER]","\uD83E\uDDF9 Route cache cleared")}getCacheStats(){return{size:this.routeCache.size,maxSize:this.options.maxCacheSize,routeStats:this.routeStats}}getOptimizationSuggestions(){let $=[],{routeStats:F}=this;if(F.paramRoutes>F.staticRoutes*2)$.push("\uD83D\uDE80 Consider grouping routes to reduce parameter route overhead");if(F.maxDepth>6)$.push("\uD83D\uDCCA Deep route nesting detected - consider flattening some routes");if(F.totalRoutes>200)$.push("\u26A1 Large route table - consider route splitting or lazy loading");if(!this.options.enableCaching&&F.totalRoutes>50)$.push("\uD83D\uDCBE Enable caching for better performance with many routes");if(F.procedureTypes.file>20)$.push("\uD83D\uDCC1 Many file routes detected - consider static file serving");if(F.procedureTypes.subscription>10)$.push("\uD83D\uDD04 Many subscription routes - ensure WebSocket optimization");return $}findRoutesByPattern($){return[]}getRouteInfo($){let F=$.split("/").filter(Boolean),J=F.join("/"),_=this.routeCache.has(J),W=performance.now(),X=this.matchDirect(F),Y=performance.now()-W;return{found:!!X,route:X?.route,params:X?.params,cached:_,matchTime:Y}}benchmark($,F=1000){let J=$.map((G)=>G.split("/").filter(Boolean)),_=new Map(this.routeCache);this.routeCache.clear();let W=performance.now();for(let G=0;G<F;G++)for(let D of J)this.matchDirect(D);let Y=performance.now()-W,j=F*$.length,Z=Y/j,L=j/Y*1000;return this.routeCache=_,{averageTime:Z,totalTime:Y,routesPerSecond:L}}warmupCache($){if(!this.options.enableCaching)return;let F=performance.now(),J=0;for(let W of $){let X=W.split("/").filter(Boolean),Y=this.matchDirect(X);if(Y)this.cacheResult(X.join("/"),Y),J++}let _=performance.now();if(this.options.debug)console.log("[ROUTEMATCHER]",`\uD83D\uDD25 Cache warmed up: ${J} routes in ${(_-F).toFixed(2)}ms`)}}var C=import.meta.require,x={NO_CACHE:{cacheControl:"no-cache, no-store, must-revalidate",expires:new Date(0).toUTCString()},ONE_YEAR:{cacheControl:"public, max-age=31536000",expires:new Date(Date.now()+31536000000).toUTCString()},IMMUTABLE:{cacheControl:"public, max-age=31536000, immutable",expires:new Date(Date.now()+31536000000).toUTCString()},ONE_MONTH:{cacheControl:"public, max-age=2592000"},ONE_WEEK:{cacheControl:"public, max-age=604800"},ONE_DAY:{cacheControl:"public, max-age=86400, must-revalidate"},ONE_HOUR:{cacheControl:"public, max-age=3600, must-revalidate"},FIVE_MINUTES:{cacheControl:"public, max-age=300"},API:{cacheControl:"public, max-age=300, s-maxage=3600"},STYLESHEET:{cacheControl:"public, max-age=2592000, must-revalidate"},JAVASCRIPT:{cacheControl:"public, max-age=2592000, must-revalidate"},HASHED_ASSET:{cacheControl:"public, max-age=31536000, immutable",expires:new Date(Date.now()+31536000000).toUTCString()},FONT:{cacheControl:"public, max-age=31536000",expires:new Date(Date.now()+31536000000).toUTCString()},IMAGE:{cacheControl:"public, max-age=2592000"},FAVICON:{cacheControl:"public, max-age=31536000",expires:new Date(Date.now()+31536000000).toUTCString()},HTML_PAGE:{cacheControl:"public, max-age=3600, must-revalidate"},API_RESPONSE:{cacheControl:"public, max-age=300, s-maxage=900"},FEED:{cacheControl:"public, max-age=3600"},SITEMAP:{cacheControl:"public, max-age=86400"},MANIFEST:{cacheControl:"public, max-age=86400, must-revalidate"},SERVICE_WORKER:{cacheControl:"no-cache, no-store, must-revalidate",expires:new Date(0).toUTCString()},MEDIA_STREAM:{cacheControl:"no-cache, no-store"},DOCUMENT:{cacheControl:"public, max-age=86400"},ARCHIVE:{cacheControl:"public, max-age=604800"}};function q($){return x[$]}var v={html:"text/html; charset=utf-8",css:"text/css",javascript:"application/javascript",json:"application/json",xml:"application/xml",text:"text/plain; charset=utf-8",csv:"text/csv",markdown:"text/markdown; charset=utf-8",png:"image/png",jpeg:"image/jpeg",gif:"image/gif",svg:"image/svg+xml",webp:"image/webp",icon:"image/x-icon",bmp:"image/bmp",woff:"font/woff",woff2:"font/woff2",truetype:"font/ttf","embedded-opentype":"application/vnd.ms-fontobject",opentype:"font/otf",mp4:"video/mp4",webm:"video/webm",ogg:"video/ogg",mpeg:"audio/mpeg",wav:"audio/wav",flac:"audio/flac",pdf:"application/pdf",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",zip:"application/zip",tar:"application/x-tar",gzip:"application/gzip","7z":"application/x-7z-compressed",js:"application/javascript",txt:"text/plain; charset=utf-8",jpg:"image/jpeg",ico:"image/x-icon",ttf:"font/ttf",eot:"application/vnd.ms-fontobject",otf:"font/otf",mp3:"audio/mpeg",gz:"application/gzip"};function S($){return v[$]||"application/octet-stream"}function f($){if(typeof Bun<"u"&&Bun.hash)return Bun.hash($).toString(16);try{let F=C("crypto"),J;if(typeof $==="string")J=Buffer.from($);else if($ instanceof ArrayBuffer)J=Buffer.from(new Uint8Array($));else J=Buffer.from($);return F.createHash("md5").update(J).digest("hex")}catch{let F=typeof $==="string"?new TextEncoder().encode($):$ instanceof ArrayBuffer?new Uint8Array($):$,J=0;for(let _=0;_<F.length;_++)J=(J<<5)-J+F[_],J=J&J;return Math.abs(J).toString(16).padStart(16,"0")}}function V($,F,J){let _=$.headers.get("Origin"),W=!0;if((J?.allowAll??!0)&&_)return _;if(F.length===0)return _||"*";if(!_)return F[0];if(F.includes(_))return _;return F[0]}class B{headers={};contentType($){return this.headers["Content-Type"]=S($),this}filePath($){let F=$.split(".").pop()?.toLowerCase()||"";return this.headers["Content-Type"]=S(F),this}mimeType($){return this.headers["Content-Type"]=$,this}cache($){let{cacheControl:F,expires:J}=q($);if(this.headers["Cache-Control"]=F,J)this.headers.Expires=J;return this}eTag($){if(!$)return this;let F;if(typeof $==="string"&&$.startsWith('"'))F=$;else F=`"${f($)}"`;return this.headers.ETag=F,this}lastModified($){return this.headers["Last-Modified"]=$.toUTCString(),this}contentLength($){return this.headers["Content-Length"]=$.toString(),this}redirect($,F=!1){return this.headers.Location=$,this.headers["X-Redirect-Type"]=F?"permanent":"temporary",this}cors({origin:$="*",methods:F=["GET","POST","PUT","DELETE","OPTIONS"],headers:J=["Content-Type","Authorization"],maxAge:_=86400,credentials:W}={}){let X=W??$!=="*";if(X&&$==="*")throw Error('CORS: Cannot use credentials with wildcard origin "*". Specify an exact origin or set credentials to false.');if(this.headers["Access-Control-Allow-Origin"]=$,this.headers["Access-Control-Allow-Methods"]=F.join(", "),this.headers["Access-Control-Allow-Headers"]=J.join(", "),X)this.headers["Access-Control-Allow-Credentials"]="true";return this.headers["Access-Control-Max-Age"]=_.toString(),this}security($){let F={hsts:!0,noSniff:!0,frameOptions:"SAMEORIGIN",xssProtection:!0,...$};if(F.csp)this.headers["Content-Security-Policy"]=F.csp;if(F.hsts){let J=typeof F.hsts==="number"?F.hsts:31536000;this.headers["Strict-Transport-Security"]=`max-age=${J}; includeSubDomains`}if(F.noSniff)this.headers["X-Content-Type-Options"]="nosniff";if(F.frameOptions)this.headers["X-Frame-Options"]=F.frameOptions;if(F.xssProtection)this.headers["X-XSS-Protection"]="1; mode=block";return this}custom($,F){return this.headers[$]=F,this}customHeaders($){return Object.assign(this.headers,$),this}vary(...$){let F=this.headers.Vary,J=F?`${F}, ${$.join(", ")}`:$.join(", ");return this.headers.Vary=J,this}compress($){if($)this.headers["Content-Encoding"]=$;return this}build($){let F={...this.headers};if($&&!F["Content-Length"]){let J=typeof $==="string"?new TextEncoder().encode($).length:$.byteLength;F["Content-Length"]=J.toString()}return F}buildHeaders($){return new Headers(this.build($))}}function A(){return new B}function l($,F){let J=A().contentType($);if(F)J.cache(F);return J.build()}var c={css:($)=>A().contentType("css").cache("STYLESHEET").eTag($),javascript:($)=>A().contentType("javascript").cache("JAVASCRIPT").eTag($),hashedAsset:($,F)=>A().filePath($).cache("HASHED_ASSET").eTag(F),font:($)=>A().filePath($).cache("FONT").cors({origin:"*"}),image:($)=>A().filePath($).cache("IMAGE"),favicon:()=>A().contentType("icon").cache("FAVICON"),api:()=>A().contentType("json").cache("API_RESPONSE").cors(),expensiveApi:($)=>A().contentType("json").cache("API_RESPONSE").cors().eTag($),realtime:()=>A().contentType("json").cache("NO_CACHE").cors(),html:()=>A().contentType("html").cache("HTML_PAGE").security(),expensiveHtml:($)=>A().contentType("html").cache("HTML_PAGE").security().eTag($),manifest:($)=>A().contentType("json").cache("MANIFEST").eTag($),serviceWorker:()=>A().contentType("javascript").cache("SERVICE_WORKER"),sitemap:($)=>A().contentType("xml").cache("SITEMAP").eTag($),feed:($,F="rss")=>A().contentType("xml").cache("FEED").eTag($).mimeType(F==="rss"?"application/rss+xml":"application/atom+xml"),video:($)=>A().filePath($).cache("MEDIA_STREAM").custom("Accept-Ranges","bytes"),audio:($)=>A().filePath($).cache("MEDIA_STREAM").custom("Accept-Ranges","bytes"),pdf:($)=>A().contentType("pdf").cache("DOCUMENT").custom("Content-Disposition","inline").eTag($),download:($,F,J)=>A().filePath(F).cache("ARCHIVE").custom("Content-Disposition",`attachment; filename="${$}"`).eTag(J),secure:()=>A().security({csp:"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'",hsts:31536000}).custom("X-Powered-By","Bun"),permanentRedirect:($)=>A().redirect($,!0),temporaryRedirect:($)=>A().redirect($,!1)};async function T($,F,J,_={}){let{maxChunkSize:W=2097152,cacheMaxAge:X=3600,acceptedExtensions:Y}=_;if(!await $.exists())return new Response("Media file not found",{status:404,headers:A().contentType("text").cors({origin:V(F,J)}).build()});if(Y&&Y.length>0){let K=$.name?.toLowerCase().split(".").pop();if(!K||!Y.includes(K))return new Response("File type not supported",{status:415,headers:A().contentType("text").cors({origin:V(F,J)}).build()})}let Z=$.size,L=F.headers.get("Range");if(!L)return new Response($,{headers:A().filePath($.name||"media").custom("Accept-Ranges","bytes").custom("Cache-Control",`public, max-age=${X}`).cors({origin:V(F,J)}).build()});let G=L.match(/bytes=(\d*)-(\d*)/);if(!G)return new Response("Invalid Range header",{status:416,headers:A().contentType("text").cors({origin:V(F,J)}).custom("Content-Range",`bytes */${Z}`).build()});let D=G[1]?parseInt(G[1]):0,N=G[2]?parseInt(G[2]):Z-1,M=Math.min(N,D+W-1,Z-1);if(D>=Z||M>=Z||D>M)return new Response("Range not satisfiable",{status:416,headers:A().contentType("text").cors({origin:V(F,J)}).custom("Content-Range",`bytes */${Z}`).build()});return new Response($.slice(D,M+1),{status:206,headers:A().filePath($.name||"media").custom("Accept-Ranges","bytes").custom("Cache-Control",`public, max-age=${X}`).cors({origin:V(F,J)}).build()})}function z($,F,J){return{type:"stream",file:$,request:F,options:J}}class E{routes;routeMatcher;contextCreator;prefix;globalMiddlewares;server=null;wsConfig;integrations;development=!0;debug=!1;allowedOrigins=[];onError;activeConnections=new Set;maxConnections=parseInt(process.env.MAX_WS_CONNECTIONS??"1000");connectionTimeout=parseInt(process.env.WS_TIMEOUT??"30000");maxRequestSize=parseInt(process.env.MAX_REQUEST_SIZE??"10485760");requestTimeout=parseInt(process.env.REQUEST_TIMEOUT??"30000");constructor($){if(this.contextCreator=$.context,this.prefix=$.prefix??"",this.routes=$.routes,this.globalMiddlewares=$.globalMiddlewares??[],this.wsConfig=$.websocket??{},this.integrations=$.integrations,this.debug=!!$.debug&&!0,this.onError=$.onError,this.routeMatcher=new Q($.routes,{enableCaching:!this.development,maxCacheSize:parseInt(process.env.ROUTE_CACHE_SIZE??"1000"),debug:this.debug}),!this.development)setInterval(()=>this.cleanupStaleConnections(),60000);if(this.debug)console.log("Router initialized with route matcher"),console.log("Route cache stats:",this.routeMatcher.getCacheStats())}findRoute($){return this.routeMatcher.match($)}async parseInput($,F,J){let _={},W=new URL($.url),X=Object.fromEntries(W.searchParams),Y=$.method;if(Y==="POST"){if(parseInt($.headers.get("content-length")||"0")>this.maxRequestSize)throw Error("Request payload too large")}if(Y==="GET"&&["query","streamQuery","file","fileStream","html"].includes(J))_=X;else if(Y==="POST"&&["mutation","formMutation","streamMutation"].includes(J)){let j=$.headers.get("content-type");if(j?.includes("application/json"))_=await $.json();else if(j?.includes("multipart/form-data")){let Z=await $.formData();_=await this.parseFormData(Z)}else throw Error("Unsupported content type")}else throw Error(`Unsupported request method ${Y} for procedure type ${J}`);return F.parse(_)}async parseFormData($){let F={},J={};$.forEach(async(_,W)=>{if(_ instanceof File){if(!J[W])J[W]=[];J[W].push(_)}else if(typeof _==="string")try{F[W]=JSON.parse(_)}catch{F[W]=_}});for(let[_,W]of Object.entries(J))F[_]=W;return F}handleError($,F){if(this.onError)try{this.onError($,{req:F,route:F.url})}catch(_){console.error("Error logger failed:",_)}let J=$?.issues??$?.errors;if(Array.isArray(J)&&$.name==="ZodError")return new Response(JSON.stringify({error:{name:"ValidationError",code:"BAD_REQUEST",clientCode:"VALIDATION_ERROR",message:"Input validation failed",data:{validationErrors:J.map((_)=>({path:_.path.join("."),message:_.message,code:_.code,received:_.received}))},httpStatus:400}}),{status:400,headers:A().contentType("json").cors({origin:V(F,this.allowedOrigins)}).build()});if($ instanceof I)return new Response(JSON.stringify({error:$.toJSON()}),{status:$.httpStatus,headers:A().contentType("json").cors({origin:V(F,this.allowedOrigins)}).build()});if($ instanceof Error){let _=$.message==="Not Found"?404:$.message==="Unauthorized"?401:$.message==="Forbidden"?403:$.message==="Request payload too large"?413:$.message==="Request timeout"?504:500,W=_===404?"NOT_FOUND":_===401?"UNAUTHORIZED":_===403?"FORBIDDEN":_===413?"PAYLOAD_TOO_LARGE":_===504?"GATEWAY_TIMEOUT":"INTERNAL_SERVER_ERROR",X=_<500?$.message:"Internal Server Error";return new Response(JSON.stringify({error:{code:W,message:X}}),{status:_,headers:A().contentType("json").cors({origin:V(F,this.allowedOrigins)}).build()})}return new Response(JSON.stringify({error:{code:"INTERNAL_SERVER_ERROR",message:"Internal Server Error"}}),{status:500,headers:A().contentType("json").cors({origin:V(F,this.allowedOrigins)}).build()})}handleFileCacheControl($){let F;if($?.includes("text/html"))F="public, max-age=0, must-revalidate";else if($?.includes("text/css")||$?.includes("text/javascript")||$?.includes("application/javascript"))F="public, max-age=31536000, immutable";else F="public, max-age=86400, stale-while-revalidate=604800";return F}async handleRequest($,F){try{if($.method==="OPTIONS")return new Response(null,{status:204,headers:A().cors({origin:V($,this.allowedOrigins)}).build()});let _=new URL($.url).pathname;if(_=_.startsWith(this.prefix)?_.slice(this.prefix.length):_,_=_.startsWith("/")?_:"/"+_,_==="/ws"&&F){if(this.debug)console.log("\uD83D\uDD0C WebSocket upgrade requested");return this.handleWebSocketUpgrade($,F)}let W=_==="/"?[]:_.split("/").filter(Boolean),X=this.findRoute(W);if(!X)return new Response("Not Found",{status:404,headers:A().contentType("text").cors({origin:V($,this.allowedOrigins)}).build()});let{route:Y}=X,j=Y._timeout??this.requestTimeout,Z=new Promise((L,G)=>{setTimeout(()=>G(Error("Request timeout")),j)});return await Promise.race([this._handleRequest($,F,X),Z])}catch(J){return this.handleError(J,$)}}async _handleRequest($,F,J){try{if(!J)return new Response("Not Found",{status:404,headers:A().contentType("text").cors({origin:V($,this.allowedOrigins)}).build()});let{params:_,route:W}=J;if(!W.handler)return new Response("Handler not implemented",{status:501,headers:A().contentType("text").cors({origin:V($,this.allowedOrigins)}).build()});let X=new Headers,Y=await this.contextCreator($);Object.assign(Y,{req:$,params:_,publishToProcedure:async(G,D,N)=>{await this.publishToProcedure(G,D,Y,N)},headers:X});for(let G of this.globalMiddlewares){let D=await G(Y);if(D!==null&&D!==void 0&&typeof D==="object")Object.assign(Y,D)}for(let G of W.middlewares){let D=await G(Y);if(D!==null&&D!==void 0&&typeof D==="object")Object.assign(Y,D)}let j=await this.parseInput($,W._input,W._type),Z=await W.handler({ctx:Y,input:j}),L=A().cors({origin:V($,this.allowedOrigins)}).build();return Object.keys(L).forEach((G)=>{X.set(G,L[G])}),await this.handleProcedureResponse(W._type,Z,X)}catch(_){return this.handleError(_,$)}}async testRequest($){return this.handleRequest($)}async handleProcedureResponse($,F,J){switch($){case"file":if(F instanceof Response)return F;let _=F.type;return J.append("Content-Type",_),J.append("Cache-Control",this.development?"public, max-age=0, must-revalidate":this.handleFileCacheControl(_)),new Response(F,{headers:J});case"fileStream":if(typeof F==="object"&&F!==null&&"type"in F&&F.type==="stream"){let X=F;return await T(F.file,F.request,this.allowedOrigins,X.options)}let W=F.type;return J.append("Content-Type",W),J.append("Cache-Control",this.development?"public, max-age=0, must-revalidate":this.handleFileCacheControl(W)),new Response(F,{headers:J});case"html":if(typeof F==="string")return J.append("Content-Type","text/html"),new Response(F,{headers:J});break;case"query":case"mutation":case"formMutation":case"streamQuery":case"streamMutation":case"subscription":default:return J.append("Content-Type","application/json"),new Response(JSON.stringify({data:F}),{headers:J})}return J.append("Content-Type","application/json"),new Response(JSON.stringify({data:F}),{headers:J})}cleanupStaleConnections(){let $=Date.now();for(let F of this.activeConnections)if($-F.data.lastActivity>this.connectionTimeout)F.close(),this.activeConnections.delete(F)}async handleWebSocketUpgrade($,F){if(this.activeConnections.size>=this.maxConnections)return new Response("Too many connections",{status:503});let J=await this.contextCreator($);Object.assign(J,{req:$});for(let W of this.globalMiddlewares){let X=await W(J);if(X!==null&&X!==void 0&&typeof X==="object")Object.assign(J,X)}if(F.upgrade($,{data:{ctx:J,topics:new Set,lastActivity:Date.now()}}))return new Response(null,{status:101});return new Response("WebSocket upgrade failed",{status:500})}async handleAuthenticate($,F,J){try{let _=F.startsWith("Bearer ")?F.substring(7):F;if($.data.ctx.authToken=_,$.send(JSON.stringify({type:"auth_success",message:"Authentication successful"})),this.debug)console.log("[BRPC]","WebSocket authenticated successfully")}catch(_){if($.send(JSON.stringify({type:"auth_error",error:_ instanceof Error?_.message:"Authentication failed"})),this.debug)console.error("WebSocket authentication error:",_)}}async handleSubscribe($,F,J){let _=this.findRoute(F.split("/").filter(Boolean));if(!_||_.route._type!=="subscription")throw Error("Invalid subscription route");$.subscribe(F),$.data.topics.add(F)}async handleUnsubscribe($,F,J){let _=this.findRoute(F.split("/").filter(Boolean));if(!_||_.route._type!=="subscription")throw Error("Invalid subscription route");$.unsubscribe(F),$.data.topics.delete(F)}async handlePublish($,F,J,_){let W=this.findRoute(F.split("/").filter(Boolean));if(!W)throw Error("Subscription not found");let{params:X,route:Y}=W;if(Y._type!=="subscription"||!Y.handler)throw Error("Invalid subscription route");Object.assign(_,{params:X});let j=Y._input.fields?Y._input.parse(J):J,Z=await Y.handler({ctx:_,input:j}),L={topic:F,data:Z,type:"publish"};if(this.debug)console.log(`Server will publish to ${F}`,L);this.server.publish(F,JSON.stringify(L))}async publishToProcedure($,F,J,_){if($._type!=="subscription"||!$.handler)throw Error("Can only publish to subscription procedures");let W=this.findRoutePath(this.routes,$);if(!W)throw Error("Subscription route not found");let X=W.split("/").map((Z)=>{if(Z.startsWith(":")){let L=Z.slice(1),G=_[L];if(!G)throw Error(`Missing parameter: ${L}`);return G}return Z}).join("/"),Y=await $.handler({ctx:{...J,params:_},input:F}),j={topic:X,data:Y,type:"publish"};if(this.debug)console.log(`Server will publish to ${X}`,j);return this.server?.publish(X,JSON.stringify(j)),Y}async publish($,F,J){if($._type!=="subscription"||!$.handler)throw Error("Can only publish to subscription procedures");let _=this.findRoutePath(this.routes,$);if(!_)throw Error("Subscription route not found");let W=_.split("/").map((j)=>{if(j.startsWith(":")){let Z=j.slice(1),L=J?J[Z]:null;if(!L)throw Error(`Missing parameter: ${Z}`);return L}return j}).join("/"),X=await $.handler({ctx:{params:J},input:F}),Y={topic:W,data:X,type:"publish"};if(this.debug)console.log(`Server will publish to ${W}`,Y);return this.server?.publish(W,JSON.stringify(Y)),X}freePublish($,F){let J={topic:$,data:F,type:"publish"};this.server?.publish($,JSON.stringify(J))}findRoutePath($,F,J=""){for(let[_,W]of Object.entries($)){let X=J?`${J}/${_}`:_;if(W===F)return X;if(W&&typeof W==="object"&&!("_type"in W)){let Y=this.findRoutePath(W,F,X);if(Y)return Y}}return null}getIntegrationRoutes(){if(!this.integrations)return;let $={};if(this.integrations.betterAuth){let F=this.integrations.betterAuth.handler;if(!F)throw Error("Please provide a handler for betterAuth integration");$["/api/auth/*"]=async(J)=>{return await F(J)}}if(this.integrations.rawRoutes)Object.keys(this.integrations.rawRoutes).forEach((F)=>{$[F]=this.integrations.rawRoutes[F]});return Object.keys($).length>0?$:void 0}getRouteStats(){return{cache:this.routeMatcher.getCacheStats(),suggestions:this.routeMatcher.getOptimizationSuggestions()}}clearRouteCache(){if(this.routeMatcher.clearCache(),this.debug)console.log("[ROUTEMATCHER] Route cache cleared")}benchmarkRoutes($=["/users/123","/posts/456/comments"],F=1000){if(this.debug){let J=this.routeMatcher.benchmark($,F);return console.log("[ROUTEMATCHER]","Route matching benchmark:",J),J}}updateRoutes($){if(this.development)this.routes=$,this.routeMatcher=new Q($,{enableCaching:!1,debug:this.debug}),console.log("[HMR]","Server Routes updated");else console.warn("Route updates are only allowed in development mode")}get url(){return this.server?.url.toString()??null}listen($,F){if(this.server=Bun.serve({port:$,routes:this.getIntegrationRoutes(),fetch:(J,_)=>this.handleRequest(J,_),websocket:{open:(J)=>{if(this.activeConnections.add(J),this.wsConfig?.onOpen)this.wsConfig.onOpen(J,J.data.ctx);if(this.debug)console.log("[BRPC]","WebSocket connection opened",{connections:this.activeConnections.size})},message:async(J,_)=>{try{J.data.lastActivity=Date.now();let{type:W,topic:X,token:Y,data:j}=JSON.parse(_),Z=J.data.ctx;if(this.debug)console.log("[BRPC]","WebSocket message received:",{type:W,topic:X});switch(W){case"authenticate":await this.handleAuthenticate(J,Y,Z);break;case"subscribe":await this.handleSubscribe(J,X,Z);break;case"unsubscribe":await this.handleUnsubscribe(J,X,Z);break;case"publish":await this.handlePublish(J,X,j,Z);break;default:if(J.send(JSON.stringify({error:"Unknown message type"})),this.debug)console.error("Unknown WebSocket message type:",W)}}catch(W){if(this.debug)console.error("[BRPC]","WebSocket message error:",W);if(W instanceof y)J.send(JSON.stringify({error:"Invalid message format"}));else if(W instanceof Error)J.send(JSON.stringify({error:W.message}))}},close:(J,_,W)=>{if(this.activeConnections.delete(J),J.data.topics.forEach((X)=>{J.unsubscribe(X)}),this.debug)console.log("[BRPC]","WebSocket connection closed",{code:_,reason:W,remainingConnections:this.activeConnections.size});if(this.wsConfig?.onClose)this.wsConfig.onClose(J,_,W,J.data.ctx)}}}),F)F()}}function e($){return new E($)}function F$($){return $}import{z as P}from"zod";var w={image:["image/jpeg","image/png","image/gif","image/webp","image/svg+xml"],video:["video/mp4","video/webm","video/ogg"],audio:["audio/mpeg","audio/ogg","audio/wav"],document:["application/pdf","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/vnd.ms-excel","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]},H=($,F="MB")=>{switch(F){case"MB":return $*1024*1024;case"KB":return $*1024;case"B":return $}},W$=({acceptedTypes:$,maxSize:F=1/0,minSize:J,messages:_={}}={})=>{let W=F?H(F):1/0,X=J?H(J):0,Y=$?Object.entries($).flatMap(([Z,L])=>{let G=Z;if(L==="*")return w[G];if(Array.isArray(L))return L;return[]}):void 0,j=P.instanceof(File,{message:_.required??"File is required"});if(j=j.refine((Z)=>Z.size<=W,_.maxSize??`File size must be less than ${F}MB`).refine((Z)=>Z.size>=X,_.minSize??`File size must be at least ${J}MB`),Y?.length)j=j.refine((Z)=>Y.includes(Z.type),_.type??`File must be of type: ${Y.join(", ")}`);return j};var Y$=($)=>{let F=$.paths.map((_)=>new RegExp(_)),J="Not Found";return async(_)=>{let X=new URL(_.req.url).pathname;if(F.some((Y)=>Y.test(X)))throw Error("Not Found")}};class R{requests=new Map;lastCleanupTime=Date.now();alertThreshold;config;constructor($){this.config={windowMs:$.windowMs,maxRequests:$.maxRequests,maxEntries:$.maxEntries??1e4,cleanupIntervalMs:$.cleanupIntervalMs??60000,message:$.message??"Too Many Requests",statusCode:$.statusCode??429,headerPrefix:$.headerPrefix??"X-RateLimit"},this.alertThreshold=this.config.maxEntries*0.8}cleanup(){let $=Date.now(),F=0;for(let[J,_]of this.requests.entries()){let W=$-_.startTime>this.config.windowMs,X=$-_.lastAccessed>this.config.windowMs*2;if(W||X)this.requests.delete(J),F++}return this.lastCleanupTime=$,F}handleMaxEntries($){let F=this.requests.size;if(Date.now()-this.lastCleanupTime>this.config.cleanupIntervalMs)this.cleanup();if(F>=this.alertThreshold)console.warn(`Rate limiter at ${Math.round(F/this.config.maxEntries*100)}% capacity`);if(this.requests.has($))return!0;return F<this.config.maxEntries}check($){if(!this.handleMaxEntries($))return{isLimited:!0,remaining:0,resetTime:Date.now()+this.config.windowMs};let F=Date.now(),J=this.requests.get($);if(!J||F-J.startTime>=this.config.windowMs)return this.requests.set($,{count:1,startTime:F,lastAccessed:F}),{isLimited:!1,remaining:this.config.maxRequests-1,resetTime:F+this.config.windowMs};J.lastAccessed=F;let _=J.count>=this.config.maxRequests;if(!_)J.count++;return{isLimited:_,remaining:Math.max(0,this.config.maxRequests-J.count),resetTime:J.startTime+this.config.windowMs}}getConfig(){return this.config}}var A$=($)=>{let F=new R($);return async(J)=>{if(J.req.headers.get("range"))return;let _=J.req.headers.get("x-forwarded-for")?.split(",")[0]||J.req.headers.get("x-real-ip")||"unknown",{isLimited:W,remaining:X,resetTime:Y}=F.check(_),{headerPrefix:j}=F.getConfig();if(J.headers.set(`${j}-Remaining`,X.toString()),J.headers.set(`${j}-Reset`,Y.toString()),W)throw J.headers.set(`${j}-Exceeded`,"true"),Error(F.getConfig().message)}};function G$($,F){return F??$}async function I$($,F){let J=performance.now();try{return await F()}catch(_){let W=performance.now();throw console.log(`[PERF] ${$} failed after ${(W-J).toFixed(4)} ms`),_}finally{let _=performance.now();console.log(`[PERF] ${$}: ${(_-J).toFixed(4)} ms`)}}export{z as streamMedia,l as quickHeaders,I$ as measure,f as hashContent,S as getMimeType,V as getCorsOrigin,e as createRouter,A$ as createRateLimiter,h as createProcedure,Y$ as createPathBlocker,G$ as createMiddleware,W$ as createFileSchema,F$ as createContext,c as commonHeaders,A as buildHeaders,E as Router,I as BRPCError};
5
8
 
6
- //# debugId=AB2F51E21528B46464756E2164756E21
9
+ //# debugId=2946F19BB1B206BE64756E2164756E21
@@ -1,4 +1,7 @@
1
1
  // @bun
2
- var V=($)=>$.startsWith("image/"),q=($)=>$.startsWith("video/"),O=($)=>$.startsWith("audio/"),A=($)=>$.startsWith("text/")||$.includes("pdf")||$.includes("word")||$.includes("sheet")||$.includes("presentation")||$.includes("opendocument")||$==="application/rtf",F=($)=>$.includes("zip")||$.includes("rar")||$.includes("tar")||$.includes("7z")||$.includes("gzip"),S=($)=>$==="application/javascript"||$==="application/json"||$==="text/html"||$==="text/css"||$==="application/xml"||$.includes("javascript"),z=($)=>$.startsWith("font/")||$.includes("font"),N=($)=>{if(!$)return"other";let W=$.toLowerCase();if(V(W))return"image";if(q(W))return"video";if(O(W))return"audio";if(A(W))return"document";if(F(W))return"archive";if(S(W))return"code";if(z(W))return"font";return"other"},x={isImage:V,isVideo:q,isAudio:O,isDocument:A,isArchive:F,isCode:S,isFont:z};function o(){let W=Buffer.from("Hello, this is a test file for S3 upload!","utf-8");return new File([W],"test.txt",{type:"text/plain"})}function _($,W){if(!$||$<1||!W||W<1)return"1:1";let j=(Q,J)=>J===0?Q:j(J,Q%J),H=j($,W);return`${$/H}:${W/H}`}function t($,W){if(!$||!W||$<1||W<1)return"1:1";let j=$/W,H=[{ratio:1,label:"1:1"},{ratio:0.8,label:"4:5"},{ratio:0.75,label:"3:4"},{ratio:0.6666666666666666,label:"2:3"},{ratio:0.5625,label:"9:16"},{ratio:1.5,label:"3:2"},{ratio:1.3333333333333333,label:"4:3"},{ratio:1.7777777777777777,label:"16:9"},{ratio:2.3333333333333335,label:"21:9"}],Q=0.03;for(let{ratio:X,label:K}of H)if(Math.abs(j-X)/X<Q)return K;let J=(X,K)=>K===0?X:J(K,X%K),Y=J($,W);return`${$/Y}:${W/Y}`}class L extends Error{code;clientCode;httpStatus;data;cause;static STATUS_MAP={BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_SUPPORTED:405,TIMEOUT:408,CONFLICT:409,PRECONDITION_FAILED:412,PAYLOAD_TOO_LARGE:413,UNPROCESSABLE_CONTENT:422,TOO_MANY_REQUESTS:429,CLIENT_CLOSED_REQUEST:499,INTERNAL_SERVER_ERROR:500,NOT_IMPLEMENTED:501,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504};constructor($){super($.message);if(this.name="BRPCError",this.code=$.code,this.clientCode=$.clientCode,this.httpStatus=L.STATUS_MAP[$.code],this.data=$.data,this.cause=$.cause,Error.captureStackTrace)Error.captureStackTrace(this,L)}toJSON(){return{name:this.name,code:this.code,clientCode:this.clientCode,message:this.message,data:this.data,httpStatus:this.httpStatus}}static badRequest($,W,j){return new L({code:"BAD_REQUEST",message:$,clientCode:W,data:j})}static unauthorized($="Unauthorized",W,j){return new L({code:"UNAUTHORIZED",message:$,clientCode:W,data:j})}static forbidden($="Forbidden",W,j){return new L({code:"FORBIDDEN",message:$,clientCode:W,data:j})}static notFound($="Not Found",W,j){return new L({code:"NOT_FOUND",message:$,clientCode:W,data:j})}static preconditionFailed($="Precondition failed",W,j){return new L({code:"NOT_FOUND",message:$,clientCode:W,data:j})}static conflict($,W,j){return new L({code:"CONFLICT",message:$,clientCode:W,data:j})}static unprocessableContent($,W,j){return new L({code:"UNPROCESSABLE_CONTENT",message:$,clientCode:W,data:j})}static tooManyRequests($="Too many requests",W,j){return new L({code:"TOO_MANY_REQUESTS",message:$,clientCode:W,data:j})}static internalServerError($="Internal Server Error",W,j){return new L({code:"INTERNAL_SERVER_ERROR",message:$,clientCode:W,data:j})}static timeout($="Request timeout",W,j){return new L({code:"TIMEOUT",message:$,clientCode:W,data:j})}}import E from"sharp";var T={xs:200,sm:400,md:800,lg:1200,xl:1920},G=1920;async function D($,W,j,H,Q){try{let{data:J,info:Y}=await E($,{limitInputPixels:1e8}).rotate().resize({width:W,height:W,fit:"inside",withoutEnlargement:!0}).webp({quality:j}).toBuffer({resolveWithObject:!0});return{buffer:J,object:{name:H.replace(/\.[^/.]+$/,".webp"),resolvedType:"image",metadata:{type:"image/webp",size:J.length,extension:".webp",width:Y.width,height:Y.height,aspectRatio:Y.width&&Y.height?Y.width/Y.height:1,aspectRatioStr:Y.width&&Y.height?_(Y.width,Y.height):"1:1",acl:Q}}}}catch(J){throw L.conflict(`Failed to optimize image: ${J instanceof Error?J.message:"Unknown error"}`)}}async function w($){if(!$)throw L.conflict("A file must be provided");let W,j="unknown.webp";if(typeof $==="string"){if(console.log("I'm going to fail here for file",$),!$.startsWith("data:"))throw console.log("I never got here"),L.conflict("Invalid base64 format");let[,H]=$.split(",");if(!H)throw L.conflict("Invalid base64 data");W=Buffer.from(H,"base64")}else{if(!x.isImage($.type))throw L.conflict("File must be an image");let H=await $.arrayBuffer();W=Buffer.from(H),j=$.name??j}return{buffer:W,name:j}}async function Q0($,{size:W,quality:j,acl:H}={}){let Q=await w($);return D(Q.buffer,W??G,j??80,Q.name,H??"public-read")}async function Y0($,{name:W="unknown.webp",size:j,quality:H,acl:Q}={}){if(!$?.length)throw L.conflict("A buffer must be provided");return D($,j??G,H??80,W,Q??"public-read")}async function I($,{quality:W=80,acl:j="public-read"}={}){let H=await w($),Q=await Promise.all(Object.entries(T).map(async([J,Y])=>{let X=await D(H.buffer,Y,W,H.name,j);return[J,X]}));return{variants:Object.fromEntries(Q)}}import m from"sharp";import{randomUUID as B}from"crypto";import{extname as C}from"path";async function k($,W,j){if(!W)return{data:null,error:Error("File not found")};let H=$.replace(/^\/+|\/+$/g,""),Q=C(W.name),J=`${B()}${Q}`,Y=`${H}/${J}`,X=`./buckets/${H}/${J}`;try{let K=await Bun.write(X,W);return{data:{key:Y,file:W,bytesWritten:K},error:null}}catch(K){return{data:null,error:Error(`Failed to upload ${W.name} to local filesystem`)}}}async function g($,W,j){if(!W||W.length===0)return[];let H=W.map((J)=>{let Y=j?.(J)??J.name;return k($,J,Y)});return await Promise.all(H)}async function c($){let W=Bun.file(`./buckets/${$}`);if(!await W.exists())return null;return W}async function h($){let W=Bun.file(`./buckets/${$}`);if(!await W.exists())return!1;return await W.delete(),!0}var U={uploadOne:k,uploadMany:g,getOne:c,deleteOne:h};async function G0($){let W=`./cache/images/${$.key}_${$.width}_${$.quality}.webp`,j=await U.getOne(W);if(j){if(!x.isImage(j.type))return null;return j}let H=$.key,Q=await U.getOne(H);if(!Q)return null;if(!x.isImage(Q.type))return null;let J=await Q.arrayBuffer(),Y=await m(J).resize($.width,null,{withoutEnlargement:!0}).webp({quality:$.quality??75}).toBuffer();await Bun.write(W,Y);let X=Bun.file(W);if(!await X.exists())return null;return X}import u from"sharp";async function q0($){if(!$?.length)return null;try{let W=await u($).metadata(),j=W.width??0,H=W.height??0,Q=W.height>0?j/H:1;return{type:`image/${W.format}`,size:W.size??0,extension:`.${W.format}`,width:j,height:H,aspectRatio:Q,aspectRatioStr:_(j,H)}}catch(W){return console.warn("Failed to read image metadata:",W),null}}var v=process.env.AWS_FOLDER,b=v?`${v}/`:"";var{s3:y}=globalThis.Bun;import{extname as d}from"path";async function R($,W,j){let H="buffer"in W?W:{buffer:W,object:{name:W.name,resolvedType:N(W.type),metadata:{type:W.type,extension:d(W.name),size:W.size,acl:j?.acl??"public-read"}}};if("buffer"in W){if(!W.object?.name||!W.object?.metadata)throw L.badRequest("Complete metadata required when uploading buffer")}let Q=$.length>1?`${$.replace(/^\/+|\/+$/g,"")}/`:"",J=H.object.metadata.extension.startsWith(".")?H.object.metadata.extension:`.${H.object.metadata.extension}`,Y=H.object.name?H.object.name:`${crypto.randomUUID()}${J}`,X=`${b}${Q}${Y}`,K=y.file(X),M=H?.object?.metadata?.acl??"public-read",Z=M!=="public-read"&&M!=="public-read-write",P=await K.write(H.buffer,{acl:M,type:H.object.metadata.type});return{uploadedBy:j?.uploadedBy,key:X,url:void 0,name:H.object.name,resolvedType:H.object.resolvedType,provider:"s3",isPrivate:Z,metadata:{...H.object.metadata,size:P,acl:M},isActive:!0}}async function I0($,W,j={throwIf:"any"}){let H=await Promise.allSettled(W.map((Y)=>R($,Y,{acl:j?.acl,uploadedBy:j?.uploadedBy}))),Q=[],J=[];if(H.forEach((Y)=>{if(Y.status==="fulfilled"){if(Q.push(Y.value),j?.externalKeys)j.externalKeys.push(Y.value.key)}else J.push(Y.reason)}),j?.throwIf==="any"&&J.length>0||j?.throwIf==="all"&&J.length===W.length)throw L.internalServerError(`Upload failed: ${J.length} of ${W.length}`);return{data:Q,errors:J.length?J:void 0}}async function f($,W,j){let H=j?.baseId??crypto.randomUUID(),Q=Object.values(W.variants).find(Boolean),J=await Promise.all(Object.entries(W.variants).map(async([X,K])=>{if(!K)return[X,null];let M=K.object.metadata.extension,Z=await R($,{...K,object:{...K.object,name:`${H}_${X}${M}`}},j);return[X,{key:Z.key,url:Z.url,name:Z.name,metadata:{type:Z.metadata?.type,extension:Z.metadata?.extension,size:Z.metadata?.size,hash:void 0,width:Z.metadata?.width,height:Z.metadata?.height,aspectRatio:Z.metadata?.aspectRatio,aspectRatioStr:Z.metadata?.aspectRatioStr,resolution:Z.metadata?.resolution,bitrate:Z.metadata?.bitrate,codec:Z.metadata?.codec,fps:Z.metadata?.fps}}]})),Y=(j?.acl??"public-read")!=="public-read"&&(j?.acl??"public-read")!=="public-read-write";return{shared:{name:Q.object.name,resolvedType:Q.object.resolvedType,provider:"s3",acl:j?.acl??"public-read",isPrivate:Y,uploadedBy:j?.uploadedBy},variants:Object.fromEntries(J.filter(([,X])=>X!==null))}}async function k0($,W,j){if(Array.isArray(W))throw Error("File can't be an array, pass a single file");let H=await I(W,{quality:j?.quality,acl:j?.acl});return f($,H,{acl:j?.acl,uploadedBy:j?.uploadedBy})}var{s3:n}=globalThis.Bun;async function p($,W){if(!$||typeof $!=="string")throw L.badRequest("Key is required");let j=$.trim();if(!j||j.length===0)throw L.badRequest("Key must be a non-empty string");try{return await n.delete($),{key:$,deleted:!0,error:null}}catch(H){return{key:$,deleted:!1,error:H}}}async function P0($,W){return(await Promise.allSettled($.map((H)=>p(H,W)))).map((H,Q)=>{if(H.status==="fulfilled")return H.value;return{key:$[Q],deleted:!1,error:H.reason}})}var{s3:s}=globalThis.Bun;async function C0($,W={debug:!0}){try{return{exists:await s.exists($),error:null}}catch(j){if(W?.debug)console.error("There was an error checking for file existance",j);return{exists:null,error:j}}}export{R as uploadOne,I0 as uploadMany,f as uploadGeneratedVariants,Y0 as optimizeImageBuffer,Q0 as optimizeImage,N as mimeTypeToResolvedType,G0 as getOptimizedImage,q0 as getImageMetadata,t as getAspectRatioStringRound,_ as getAspectRatioString,I as generateVariants,o as generateTestFile,k0 as generateAndUploadImage,x as fileTypePredicates,p as deleteOne,P0 as deleteMany,C0 as checkFileExistance};
2
+ var V=($)=>$.startsWith("image/"),q=($)=>$.startsWith("video/"),O=($)=>$.startsWith("audio/"),A=($)=>$.startsWith("text/")||$.includes("pdf")||$.includes("word")||$.includes("sheet")||$.includes("presentation")||$.includes("opendocument")||$==="application/rtf",F=($)=>$.includes("zip")||$.includes("rar")||$.includes("tar")||$.includes("7z")||$.includes("gzip"),S=($)=>$==="application/javascript"||$==="application/json"||$==="text/html"||$==="text/css"||$==="application/xml"||$.includes("javascript"),z=($)=>$.startsWith("font/")||$.includes("font"),N=($)=>{if(!$)return"other";let W=$.toLowerCase();if(V(W))return"image";if(q(W))return"video";if(O(W))return"audio";if(A(W))return"document";if(F(W))return"archive";if(S(W))return"code";if(z(W))return"font";return"other"},x={isImage:V,isVideo:q,isAudio:O,isDocument:A,isArchive:F,isCode:S,isFont:z};function o(){let W=Buffer.from("Hello, this is a test file for S3 upload!","utf-8");return new File([W],"test.txt",{type:"text/plain"})}function _($,W){if(!$||$<1||!W||W<1)return"1:1";let j=(Q,J)=>J===0?Q:j(J,Q%J),H=j($,W);return`${$/H}:${W/H}`}function t($,W){if(!$||!W||$<1||W<1)return"1:1";let j=$/W,H=[{ratio:1,label:"1:1"},{ratio:0.8,label:"4:5"},{ratio:0.75,label:"3:4"},{ratio:0.6666666666666666,label:"2:3"},{ratio:0.5625,label:"9:16"},{ratio:1.5,label:"3:2"},{ratio:1.3333333333333333,label:"4:3"},{ratio:1.7777777777777777,label:"16:9"},{ratio:2.3333333333333335,label:"21:9"}],Q=0.03;for(let{ratio:X,label:K}of H)if(Math.abs(j-X)/X<Q)return K;let J=(X,K)=>K===0?X:J(K,X%K),Y=J($,W);return`${$/Y}:${W/Y}`}class L extends Error{code;clientCode;httpStatus;data;cause;static STATUS_MAP={BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_SUPPORTED:405,TIMEOUT:408,CONFLICT:409,PRECONDITION_FAILED:412,PAYLOAD_TOO_LARGE:413,UNPROCESSABLE_CONTENT:422,TOO_MANY_REQUESTS:429,CLIENT_CLOSED_REQUEST:499,INTERNAL_SERVER_ERROR:500,NOT_IMPLEMENTED:501,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504};constructor($){super($.message);if(this.name="BRPCError",this.code=$.code,this.clientCode=$.clientCode,this.httpStatus=L.STATUS_MAP[$.code],this.data=$.data,this.cause=$.cause,Error.captureStackTrace)Error.captureStackTrace(this,L)}toJSON(){return{name:this.name,code:this.code,clientCode:this.clientCode,message:this.message,data:this.data,httpStatus:this.httpStatus}}static badRequest($,W,j){return new L({code:"BAD_REQUEST",message:$,clientCode:W,data:j})}static unauthorized($="Unauthorized",W,j){return new L({code:"UNAUTHORIZED",message:$,clientCode:W,data:j})}static forbidden($="Forbidden",W,j){return new L({code:"FORBIDDEN",message:$,clientCode:W,data:j})}static notFound($="Not Found",W,j){return new L({code:"NOT_FOUND",message:$,clientCode:W,data:j})}static preconditionFailed($="Precondition failed",W,j){//! Intentionally maps to NOT_FOUND instead of PRECONDITION_FAILED — this was changed to fix a bug
3
+ //! but the exact cause is unknown. Suspected: some client/proxy/middleware misbehaved on 412
4
+ //! (not a browser auto-retry issue — 412 doesn't trigger those). Investigate before changing back.
5
+ return new L({code:"NOT_FOUND",message:$,clientCode:W,data:j})}static conflict($,W,j){return new L({code:"CONFLICT",message:$,clientCode:W,data:j})}static unprocessableContent($,W,j){return new L({code:"UNPROCESSABLE_CONTENT",message:$,clientCode:W,data:j})}static tooManyRequests($="Too many requests",W,j){return new L({code:"TOO_MANY_REQUESTS",message:$,clientCode:W,data:j})}static internalServerError($="Internal Server Error",W,j){return new L({code:"INTERNAL_SERVER_ERROR",message:$,clientCode:W,data:j})}static timeout($="Request timeout",W,j){return new L({code:"TIMEOUT",message:$,clientCode:W,data:j})}}import E from"sharp";var T={xs:200,sm:400,md:800,lg:1200,xl:1920},G=1920;async function D($,W,j,H,Q){try{let{data:J,info:Y}=await E($,{limitInputPixels:1e8}).rotate().resize({width:W,height:W,fit:"inside",withoutEnlargement:!0}).webp({quality:j}).toBuffer({resolveWithObject:!0});return{buffer:J,object:{name:H.replace(/\.[^/.]+$/,".webp"),resolvedType:"image",metadata:{type:"image/webp",size:J.length,extension:".webp",width:Y.width,height:Y.height,aspectRatio:Y.width&&Y.height?Y.width/Y.height:1,aspectRatioStr:Y.width&&Y.height?_(Y.width,Y.height):"1:1",acl:Q}}}}catch(J){throw L.conflict(`Failed to optimize image: ${J instanceof Error?J.message:"Unknown error"}`)}}async function w($){if(!$)throw L.conflict("A file must be provided");let W,j="unknown.webp";if(typeof $==="string"){if(console.log("I'm going to fail here for file",$),!$.startsWith("data:"))throw console.log("I never got here"),L.conflict("Invalid base64 format");let[,H]=$.split(",");if(!H)throw L.conflict("Invalid base64 data");W=Buffer.from(H,"base64")}else{if(!x.isImage($.type))throw L.conflict("File must be an image");let H=await $.arrayBuffer();W=Buffer.from(H),j=$.name??j}return{buffer:W,name:j}}async function Q0($,{size:W,quality:j,acl:H}={}){let Q=await w($);return D(Q.buffer,W??G,j??80,Q.name,H??"public-read")}async function Y0($,{name:W="unknown.webp",size:j,quality:H,acl:Q}={}){if(!$?.length)throw L.conflict("A buffer must be provided");return D($,j??G,H??80,W,Q??"public-read")}async function I($,{quality:W=80,acl:j="public-read"}={}){let H=await w($),Q=await Promise.all(Object.entries(T).map(async([J,Y])=>{let X=await D(H.buffer,Y,W,H.name,j);return[J,X]}));return{variants:Object.fromEntries(Q)}}import m from"sharp";import{randomUUID as B}from"crypto";import{extname as C}from"path";async function k($,W,j){if(!W)return{data:null,error:Error("File not found")};let H=$.replace(/^\/+|\/+$/g,""),Q=C(W.name),J=`${B()}${Q}`,Y=`${H}/${J}`,X=`./buckets/${H}/${J}`;try{let K=await Bun.write(X,W);return{data:{key:Y,file:W,bytesWritten:K},error:null}}catch(K){return{data:null,error:Error(`Failed to upload ${W.name} to local filesystem`)}}}async function g($,W,j){if(!W||W.length===0)return[];let H=W.map((J)=>{let Y=j?.(J)??J.name;return k($,J,Y)});return await Promise.all(H)}async function c($){let W=Bun.file(`./buckets/${$}`);if(!await W.exists())return null;return W}async function h($){let W=Bun.file(`./buckets/${$}`);if(!await W.exists())return!1;return await W.delete(),!0}var U={uploadOne:k,uploadMany:g,getOne:c,deleteOne:h};async function G0($){let W=`./cache/images/${$.key}_${$.width}_${$.quality}.webp`,j=await U.getOne(W);if(j){if(!x.isImage(j.type))return null;return j}let H=$.key,Q=await U.getOne(H);if(!Q)return null;if(!x.isImage(Q.type))return null;let J=await Q.arrayBuffer(),Y=await m(J).resize($.width,null,{withoutEnlargement:!0}).webp({quality:$.quality??75}).toBuffer();await Bun.write(W,Y);let X=Bun.file(W);if(!await X.exists())return null;return X}import u from"sharp";async function q0($){if(!$?.length)return null;try{let W=await u($).metadata(),j=W.width??0,H=W.height??0,Q=W.height>0?j/H:1;return{type:`image/${W.format}`,size:W.size??0,extension:`.${W.format}`,width:j,height:H,aspectRatio:Q,aspectRatioStr:_(j,H)}}catch(W){return console.warn("Failed to read image metadata:",W),null}}var v=process.env.AWS_FOLDER,R=v?`${v}/`:"";var{s3:y}=globalThis.Bun;import{extname as d}from"path";async function b($,W,j){let H="buffer"in W?W:{buffer:W,object:{name:W.name,resolvedType:N(W.type),metadata:{type:W.type,extension:d(W.name),size:W.size,acl:j?.acl??"public-read"}}};if("buffer"in W){if(!W.object?.name||!W.object?.metadata)throw L.badRequest("Complete metadata required when uploading buffer")}let Q=$.length>1?`${$.replace(/^\/+|\/+$/g,"")}/`:"",J=H.object.metadata.extension.startsWith(".")?H.object.metadata.extension:`.${H.object.metadata.extension}`,Y=H.object.name?H.object.name:`${crypto.randomUUID()}${J}`,X=`${R}${Q}${Y}`,K=y.file(X),M=H?.object?.metadata?.acl??"public-read",Z=M!=="public-read"&&M!=="public-read-write",P=await K.write(H.buffer,{acl:M,type:H.object.metadata.type});return{uploadedBy:j?.uploadedBy,key:X,url:void 0,name:H.object.name,resolvedType:H.object.resolvedType,provider:"s3",isPrivate:Z,metadata:{...H.object.metadata,size:P,acl:M},isActive:!0}}async function I0($,W,j={throwIf:"any"}){let H=await Promise.allSettled(W.map((Y)=>b($,Y,{acl:j?.acl,uploadedBy:j?.uploadedBy}))),Q=[],J=[];if(H.forEach((Y)=>{if(Y.status==="fulfilled"){if(Q.push(Y.value),j?.externalKeys)j.externalKeys.push(Y.value.key)}else J.push(Y.reason)}),j?.throwIf==="any"&&J.length>0||j?.throwIf==="all"&&J.length===W.length)throw L.internalServerError(`Upload failed: ${J.length} of ${W.length}`);return{data:Q,errors:J.length?J:void 0}}async function f($,W,j){let H=j?.baseId??crypto.randomUUID(),Q=Object.values(W.variants).find(Boolean),J=await Promise.all(Object.entries(W.variants).map(async([X,K])=>{if(!K)return[X,null];let M=K.object.metadata.extension,Z=await b($,{...K,object:{...K.object,name:`${H}_${X}${M}`}},j);return[X,{key:Z.key,url:Z.url,name:Z.name,metadata:{type:Z.metadata?.type,extension:Z.metadata?.extension,size:Z.metadata?.size,hash:void 0,width:Z.metadata?.width,height:Z.metadata?.height,aspectRatio:Z.metadata?.aspectRatio,aspectRatioStr:Z.metadata?.aspectRatioStr,resolution:Z.metadata?.resolution,bitrate:Z.metadata?.bitrate,codec:Z.metadata?.codec,fps:Z.metadata?.fps}}]})),Y=(j?.acl??"public-read")!=="public-read"&&(j?.acl??"public-read")!=="public-read-write";return{shared:{name:Q.object.name,resolvedType:Q.object.resolvedType,provider:"s3",acl:j?.acl??"public-read",isPrivate:Y,uploadedBy:j?.uploadedBy},variants:Object.fromEntries(J.filter(([,X])=>X!==null))}}async function k0($,W,j){if(Array.isArray(W))throw Error("File can't be an array, pass a single file");let H=await I(W,{quality:j?.quality,acl:j?.acl});return f($,H,{acl:j?.acl,uploadedBy:j?.uploadedBy})}var{s3:n}=globalThis.Bun;async function p($,W){if(!$||typeof $!=="string")throw L.badRequest("Key is required");let j=$.trim();if(!j||j.length===0)throw L.badRequest("Key must be a non-empty string");try{return await n.delete($),{key:$,deleted:!0,error:null}}catch(H){return{key:$,deleted:!1,error:H}}}async function P0($,W){return(await Promise.allSettled($.map((H)=>p(H,W)))).map((H,Q)=>{if(H.status==="fulfilled")return H.value;return{key:$[Q],deleted:!1,error:H.reason}})}var{s3:s}=globalThis.Bun;async function C0($,W={debug:!0}){try{return{exists:await s.exists($),error:null}}catch(j){if(W?.debug)console.error("There was an error checking for file existance",j);return{exists:null,error:j}}}export{b as uploadOne,I0 as uploadMany,f as uploadGeneratedVariants,Y0 as optimizeImageBuffer,Q0 as optimizeImage,N as mimeTypeToResolvedType,G0 as getOptimizedImage,q0 as getImageMetadata,t as getAspectRatioStringRound,_ as getAspectRatioString,I as generateVariants,o as generateTestFile,k0 as generateAndUploadImage,x as fileTypePredicates,p as deleteOne,P0 as deleteMany,C0 as checkFileExistance};
3
6
 
4
- //# debugId=A02A7765CCE245F964756E2164756E21
7
+ //# debugId=3015798C00FC771964756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mateosuarezdev/brpc",
3
- "version": "1.0.79",
3
+ "version": "1.0.80",
4
4
  "description": "A Type-Safe, Flexible Web application framework for Bun",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",