@sx3/ultra 0.0.4 → 0.1.1

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
@@ -126,7 +126,7 @@ const app = new Ultra()
126
126
  return context.auth.user; // Access authenticated user
127
127
  }),
128
128
  },
129
- }));
129
+ }), [isAuthenticated]); // Apply middleware scoped to routes
130
130
  ```
131
131
 
132
132
  ## Validation
@@ -205,17 +205,24 @@ You can extend the context with a function or value. Example from session module
205
205
 
206
206
  ```ts
207
207
  // session.ts
208
-
209
208
  export function createSessionModule<S extends Record<string, SessionStoreFactory>>(config: SessionConfig<S>) {
210
- // Create module
211
209
  return new Ultra()
212
- // deriveWS to add sessionId to WebSocket data object | run each socket connection | use for store data in WS connection
213
- .deriveWS((context: HTTPContext) => ({ sessionId: Session.getOrCreateId(context.request, config) }))
214
- // derive function add session instance to context | run each request
210
+ // Every socket connection
211
+ .deriveUpgrade((context) => {
212
+ const id = Session.getOrCreateId((context as HTTPContext).request, config);
213
+ return {
214
+ headers: { 'Set-Cookie': new Cookie(config.name, sign(id, config.secret), config.cookie).toString() },
215
+ data: { sessionId: id },
216
+ };
217
+ })
218
+ // Every request
215
219
  .derive(context => ({ session: new Session(config, context) }))
216
- // Middleware to initiate and commit session on each request
217
220
  .use(async ({ context, next }) => {
218
- await context.session.initiate();
221
+ if (isWS(context)) {
222
+ // context.ws.data.sessionId typed by string
223
+ }
224
+
225
+ await context.session.initiate(); // Fully typed
219
226
  const response = await next();
220
227
  await context.session.commit();
221
228
  return response;
@@ -229,11 +236,23 @@ You can add a static value for each request:
229
236
  const app = new Ultra().derive({ appName: 'My Ultra App' });
230
237
  ```
231
238
 
239
+ Context can be extended by other modules:
240
+
241
+ ```ts
242
+ const app = new Ultra()
243
+ .use(session) // session module adds `session` to context
244
+ .use(auth) // auth module adds `auth` to context
245
+ .use(({ context: { auth, session }, next }) => {
246
+ // auth and session fully typed
247
+ return next();
248
+ });
249
+ ```
250
+
232
251
  ## Core concepts
233
252
 
234
253
  ### Modules
235
254
 
236
- Each module is a self-contained application.
255
+ Each module is a self-contained application:
237
256
 
238
257
  ```ts
239
258
  // auth.ts
@@ -261,7 +280,7 @@ const main = new Ultra()
261
280
  .start();
262
281
  ```
263
282
 
264
- You can use modules as many times as you like.
283
+ You can use modules as many times as you like:
265
284
 
266
285
  ```ts
267
286
  const moduleA = new Ultra();
@@ -396,7 +415,7 @@ import { createCORSMiddleware } from '@sx3/ultra/cors';
396
415
  const cors = createCORSMiddleware({
397
416
  origin: ['http://localhost:5173'],
398
417
  credentials: true,
399
- // methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
418
+ // methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
400
419
  // allowedHeaders: ['Content-Type', 'Authorization'],
401
420
  // exposedHeaders: ['X-Custom-Header'],
402
421
  // maxAge: 3600,
@@ -407,7 +426,7 @@ const app = new Ultra().use(cors); // Apply CORS middleware globally
407
426
 
408
427
  ### Sessions
409
428
 
410
- Multiple session stores are supported: in-memory, Redis, and custom stores.
429
+ Multiple session stores are supported: in-memory, Redis, and your custom stores.
411
430
 
412
431
  ```ts
413
432
  // session.ts
@@ -415,7 +434,7 @@ import { env } from '#app/env';
415
434
  import { createSessionModule, defineConfig, MemorySessionStore, RedisSessionStore } from '@/sx3/ultra/session';
416
435
 
417
436
  export const config = defineConfig({
418
- // Name for cookie or prefix for redis key
437
+ // Name for cookie or prefix for redis key and session cookie
419
438
  name: 'session',
420
439
  ttl: 3600, // 1 hour
421
440
  store: 'redis',
@@ -451,12 +470,11 @@ const app = new Ultra().use(session).routes(input => ({
451
470
 
452
471
  ### Authentication
453
472
 
454
- Auth module requires [session module](#sessions).
455
-
456
473
  ```ts
457
474
  // auth.ts
458
475
  import { Ultra } from '@sx3/ultra';
459
476
  import { createAuthModule, defineConfig, SessionAuthProvider } from '@sx3/ultra/auth';
477
+ import type { SessionContext } from '@sx3/ultra/session';
460
478
 
461
479
  interface User {
462
480
  name: string;
@@ -466,7 +484,7 @@ interface User {
466
484
  const config = defineConfig<User>({
467
485
  provider: 'session',
468
486
  providers: {
469
- session: context => new SessionAuthProvider<User>(context),
487
+ session: context => new SessionAuthProvider<User>(context as SessionContext),
470
488
  },
471
489
  });
472
490
 
package/dist/auth.d.mts CHANGED
@@ -1,9 +1,9 @@
1
- import { t as BaseContext } from "./context-BZAzgugo.mjs";
2
- import { o as Promisable } from "./types-Cn69QrjS.mjs";
3
- import "./http-NhhQxMv8.mjs";
4
- import { t as Middleware } from "./middleware-DB4PWjb4.mjs";
5
- import "./ultra-Dawe5ndl.mjs";
6
- import { Session, SessionContext } from "./session.mjs";
1
+ import { o as Promisable } from "./types-DaFEaXF4.mjs";
2
+ import { t as BaseContext } from "./context-iXF4Voqe.mjs";
3
+ import "./http-AGiFONuJ.mjs";
4
+ import { t as Middleware } from "./middleware-CxmfdaxU.mjs";
5
+ import { ProceduresMap, Ultra } from "./ultra.mjs";
6
+ import { SessionContext } from "./session.mjs";
7
7
 
8
8
  //#region src/auth.d.ts
9
9
  interface AuthProvider<User> {
@@ -13,24 +13,26 @@ interface AuthProvider<User> {
13
13
  logout: () => Promisable<void>;
14
14
  setUser: (user: User) => Promisable<void>;
15
15
  }
16
- type AuthProviderFactory<User = any> = (context: SessionContext) => AuthProvider<User>;
16
+ type AuthProviderFactory<User = any> = (context: BaseContext) => AuthProvider<User>;
17
17
  interface AuthConfig<P extends Record<string, AuthProviderFactory> = Record<string, AuthProviderFactory>> {
18
18
  provider: keyof P;
19
19
  providers: P;
20
20
  }
21
21
  type AuthContext<User> = BaseContext & {
22
- session: Session<any>;
23
22
  auth: Auth<User>;
24
23
  };
25
24
  declare function defineConfig<User, P extends Record<string, AuthProviderFactory<User>> = Record<string, AuthProviderFactory<User>>>(config: AuthConfig<P>): AuthConfig<P>;
25
+ declare function createAuthModule<User, P extends Record<string, AuthProviderFactory<User>> = Record<string, AuthProviderFactory<User>>>(config: AuthConfig<P>): Ultra<ProceduresMap, unknown, BaseContext<unknown> & {
26
+ auth: Auth<User, P>;
27
+ }>;
26
28
  declare const isAuthenticated: Middleware<any, any, AuthContext<any>>;
27
29
  declare const isGuest: Middleware<any, any, AuthContext<any>>;
28
30
  declare class Auth<User, Providers extends Record<string, AuthProviderFactory<User>> = Record<string, AuthProviderFactory<User>>> {
29
31
  protected readonly config: AuthConfig<Providers>;
30
- protected readonly context: SessionContext;
32
+ protected readonly context: BaseContext;
31
33
  protected readonly usingProvider: keyof Providers;
32
34
  protected readonly providerCache: Map<keyof Providers, AuthProvider<User>>;
33
- constructor(config: AuthConfig<Providers>, context: SessionContext, provider?: keyof Providers, providerCache?: Map<keyof Providers, AuthProvider<User>>);
35
+ constructor(config: AuthConfig<Providers>, context: BaseContext, provider?: keyof Providers, providerCache?: Map<keyof Providers, AuthProvider<User>>);
34
36
  use(provider: keyof Providers): Auth<User, Providers>;
35
37
  get user(): User | null;
36
38
  check(): Promisable<boolean>;
@@ -50,4 +52,4 @@ declare class SessionAuthProvider<User> implements AuthProvider<User> {
50
52
  setUser(user: User): void;
51
53
  }
52
54
  //#endregion
53
- export { Auth, AuthContext, AuthProvider, SessionAuthProvider, defineConfig, isAuthenticated, isGuest };
55
+ export { Auth, AuthContext, AuthProvider, SessionAuthProvider, createAuthModule, defineConfig, isAuthenticated, isGuest };
package/dist/auth.mjs CHANGED
@@ -1 +1 @@
1
- import{r as e}from"./error-Dq1biCC8.mjs";function t(e){return e}const n=async t=>await t.context.auth.check()?t.next():new e,r=async t=>await t.context.auth.check()?new e:t.next();var i=class e{config;context;usingProvider;providerCache;constructor(e,t,n=e.provider,r=new Map){this.config=e,this.context=t,this.usingProvider=n,this.providerCache=r}use(t){return new e(this.config,this.context,t,this.providerCache)}get user(){return this.provider.user}check(){return this.provider.check()}login(e){return this.provider.login(e)}logout(){return this.provider.logout()}setUser(e){return this.provider.setUser(e)}get provider(){let e=this.providerCache.get(this.usingProvider);if(e)return e;let t=this.config.providers[this.usingProvider];if(!t)throw Error(`Auth provider "${String(this.usingProvider)}" is not configured.`);let n=t(this.context);return this.providerCache.set(this.usingProvider,n),n}},a=class{context;sessionKey;constructor(e,t=`user`){this.context=e,this.sessionKey=t}get user(){return this.context.session.get(this.sessionKey)||null}check(){return this.user!==null}login(e){this.setUser(e),this.context.session.regenerate()}logout(){this.context.session.delete(this.sessionKey)}setUser(e){this.context.session.set(this.sessionKey,e)}};export{i as Auth,a as SessionAuthProvider,t as defineConfig,n as isAuthenticated,r as isGuest};
1
+ import{r as e}from"./error-Dq1biCC8.mjs";import{Ultra as t}from"./ultra.mjs";function n(e){return e}function r(e){return new t().derive(t=>({auth:new o(e,t)}))}const i=async t=>await t.context.auth.check()?t.next():new e,a=async t=>await t.context.auth.check()?new e:t.next();var o=class e{config;context;usingProvider;providerCache;constructor(e,t,n=e.provider,r=new Map){this.config=e,this.context=t,this.usingProvider=n,this.providerCache=r}use(t){return new e(this.config,this.context,t,this.providerCache)}get user(){return this.provider.user}check(){return this.provider.check()}login(e){return this.provider.login(e)}logout(){return this.provider.logout()}setUser(e){return this.provider.setUser(e)}get provider(){let e=this.providerCache.get(this.usingProvider);if(e)return e;let t=this.config.providers[this.usingProvider];if(!t)throw Error(`Auth provider "${String(this.usingProvider)}" is not configured.`);let n=t(this.context);return this.providerCache.set(this.usingProvider,n),n}},s=class{context;sessionKey;constructor(e,t=`user`){this.context=e,this.sessionKey=t}get user(){return this.context.session.get(this.sessionKey)||null}check(){return this.user!==null}login(e){this.setUser(e),this.context.session.regenerate()}logout(){this.context.session.delete(this.sessionKey)}setUser(e){this.context.session.set(this.sessionKey,e)}};export{o as Auth,s as SessionAuthProvider,r as createAuthModule,n as defineConfig,i as isAuthenticated,a as isGuest};
package/dist/client.d.mts CHANGED
@@ -1,7 +1,7 @@
1
- import "./context-BZAzgugo.mjs";
2
- import "./http-NhhQxMv8.mjs";
3
- import { i as Procedure } from "./middleware-DB4PWjb4.mjs";
4
- import { n as Ultra, t as ProceduresMap } from "./ultra-Dawe5ndl.mjs";
1
+ import "./context-iXF4Voqe.mjs";
2
+ import "./http-AGiFONuJ.mjs";
3
+ import { i as Procedure } from "./middleware-CxmfdaxU.mjs";
4
+ import { ProceduresMap, Ultra } from "./ultra.mjs";
5
5
 
6
6
  //#region src/client.d.ts
7
7
  type GetProcedures<T> = T extends Ultra<infer P, any, any> ? P : never;
package/dist/client.mjs CHANGED
@@ -1 +1 @@
1
- function e(t,n=[]){return new Proxy(()=>{},{get(r,i){if(typeof i==`string`)return e(t,[...n,i])},apply(e,r,i){if(!n.length)throw Error(`Cannot call client root; select a procedure first`);let a=n.join(`/`),o=i[0],s=i[1];return t(a,o,s)}})}function t(...e){let t=new Headers;for(let n of e)n&&new Headers(n).forEach((e,n)=>t.set(n,e));return t}function n(n){return e(async(e,r,i)=>{let a={...n,...i},o=a?.timeout||1e4,s=new AbortController,c=a?.method||`POST`,l=`${a.baseUrl}/${e}`,u=t(n?.headers,a?.headers,i?.headers),d=null,f=setTimeout(()=>s.abort(`Timeout: ${o}`),o);switch(!0){case c===`GET`:{if(d=null,!r)break;if(typeof r!=`object`)throw Error(`GET requests params to be an object for query string generation`);let e=Object.entries(r).filter(([,e])=>e!=null).map(([e,t])=>[e,String(t)]),t=new URLSearchParams(e).toString();t&&(l+=`?${t}`);break}case r instanceof FormData:d=r;break;case typeof r==`string`:u.set(`Content-Type`,`text/plain`),d=r;break;default:u.set(`Content-Type`,`application/json`),d=JSON.stringify(r)}try{let e=await fetch(l,{method:c,...d&&{body:d},...a,signal:s.signal,headers:u});if(!e.ok)throw Error(`HTTP error: ${e.statusText} ${e.status} `);let t=e.headers.get(`Content-Type`)||``;switch(!0){case t.includes(`application/json`):return await e.json();case t.includes(`text/`):return await e.text();default:return await e.blob()}}catch(e){throw e.name===`AbortError`?Error(`Request aborted: ${e.message}`):e}finally{clearTimeout(f)}})}function r(t){let{timeout:n=1e4}=t,r=()=>Math.random().toString(36);return e((e,i,a)=>{let o=t.socket();if(!o||o.readyState!==WebSocket.OPEN)return Promise.reject(Error(`WebSocket is not open`));let{promise:s,resolve:c,reject:l}=Promise.withResolvers(),u=a?.timeout??n,d,f=r(),p=e=>{try{let t=JSON.parse(e.data);return t.id===f?(clearTimeout(d),o.removeEventListener(`message`,p),`error`in t?l(t.error):c(t.result)):void 0}catch(e){l(e)}};return o.addEventListener(`message`,p),d=setTimeout(()=>{o.removeEventListener(`message`,p),l(`Timeout: ${u}`)},u),o.send(JSON.stringify({id:f,method:e,params:i})),s})}function i(t){return e((e,n,r)=>{let i=t.pick(e,n,r),a=e.split(`/`).filter(Boolean),o=i;for(let e of a)o[e]&&(o=o[e]);return o(n,r)})}export{n as createHTTPClient,i as createSuperClient,r as createWebSocketClient};
1
+ function e(t,n=[]){return new Proxy(()=>{},{get(r,i){if(typeof i==`string`)return e(t,[...n,i])},apply(e,r,i){if(!n.length)throw Error(`Cannot call client root; select a procedure first`);let a=n.join(`/`),o=i[0],s=i[1];return t(a,o,s)}})}function t(...e){let t=new Headers;for(let n of e)n&&new Headers(n).forEach((e,n)=>t.set(n,e));return t}function n(n){return e(async(e,r,i)=>{let a={...n,...i},o=a?.timeout||1e4,s=new AbortController,c=a?.method||`POST`,l=`${a.baseUrl}/${e}`,u=t(n?.headers,a?.headers,i?.headers),d=null,f=setTimeout(()=>s.abort(`Timeout: ${o}`),o);switch(!0){case c===`GET`:{if(d=null,!r)break;if(typeof r!=`object`)throw Error(`GET requests params to be an object for query string generation`);let e=Object.entries(r).filter(([,e])=>e!=null).map(([e,t])=>[e,String(t)]),t=new URLSearchParams(e).toString();t&&(l+=`?${t}`);break}case r instanceof FormData:d=r;break;case typeof r==`string`:u.set(`Content-Type`,`text/plain`),d=r;break;default:u.set(`Content-Type`,`application/json`),d=JSON.stringify(r)}try{let e=await fetch(l,{method:c,...d&&{body:d},...a,signal:s.signal,headers:u});if(!e.ok)throw Error(`${e.statusText} ${e.status}`);let t=e.headers.get(`Content-Type`)||``;switch(!0){case e.status===204:return null;case t.startsWith(`application/json`):return await e.json();case t.startsWith(`text/`):return await e.text();default:return await e.blob()}}catch(e){throw e.name===`AbortError`?Error(`Request aborted: ${e.message}`):e}finally{clearTimeout(f)}})}function r(t){let{timeout:n=1e4}=t,r=()=>Math.random().toString(36);return e((e,i,a)=>{let o=t.socket();if(!o||o.readyState!==WebSocket.OPEN)return Promise.reject(Error(`WebSocket is not open`));let{promise:s,resolve:c,reject:l}=Promise.withResolvers(),u=a?.timeout??n,d,f=r(),p=e=>{try{let t=JSON.parse(e.data);return t.id===f?(clearTimeout(d),o.removeEventListener(`message`,p),`error`in t?l(t.error):c(t.result)):void 0}catch(e){l(e)}};return o.addEventListener(`message`,p),d=setTimeout(()=>{o.removeEventListener(`message`,p),l(`Timeout: ${u}`)},u),o.send(JSON.stringify({id:f,method:e,params:i})),s})}function i(t){return e((e,n,r)=>{let i=t.pick(e,n,r),a=e.split(`/`).filter(Boolean),o=i;for(let e of a)o[e]&&(o=o[e]);return o(n,r)})}export{n as createHTTPClient,i as createSuperClient,r as createWebSocketClient};
@@ -0,0 +1,36 @@
1
+ import { o as Promisable } from "./types-DaFEaXF4.mjs";
2
+ import { BunRequest, Server, ServerWebSocket } from "bun";
3
+
4
+ //#region src/context.d.ts
5
+ type DeriveFunction<C> = (context: C) => Promisable<Record<PropertyKey, any>>;
6
+ type DeriveValue<C> = DeriveFunction<C> | Record<PropertyKey, any>;
7
+ type ExtractDerive<C, T extends DeriveValue<C>> = T extends ((...args: any[]) => any) ? Awaited<ReturnType<T>> : T;
8
+ type DeriveUpgradePossibleValue = {
9
+ data: Record<PropertyKey, any>;
10
+ } | {
11
+ headers: Record<string, string>;
12
+ } | {
13
+ data: Record<PropertyKey, any>;
14
+ headers: Record<string, string>;
15
+ };
16
+ type DeriveUpgradeFunction<C> = (context: C) => Promisable<DeriveUpgradePossibleValue>;
17
+ type DeriveUpgradeValue<C> = DeriveUpgradeFunction<C> | DeriveUpgradePossibleValue;
18
+ type ExtractDeriveUpgradeData<C, T extends DeriveUpgradeValue<C>> = T extends ((...args: any[]) => any) ? Awaited<ReturnType<T>> extends {
19
+ data: infer D;
20
+ } ? D : never : T extends {
21
+ data: infer D;
22
+ } ? D : never;
23
+ type RebindSocketData<C, SD> = C extends HTTPContext<any> ? HTTPContext<SD> & Omit<C, keyof HTTPContext<any>> : C extends WSContext<any> ? WSContext<SD> & Omit<C, keyof WSContext<any>> : C extends BaseContext<any> ? BaseContext<SD> & Omit<C, keyof BaseContext<any>> : C;
24
+ interface HTTPContext<SD = unknown> {
25
+ server: Server<SD>;
26
+ request: BunRequest;
27
+ }
28
+ interface WSContext<SD = unknown> {
29
+ server: Server<SD>;
30
+ ws: ServerWebSocket<SD>;
31
+ }
32
+ type BaseContext<SD = unknown> = HTTPContext<SD> | WSContext<SD>;
33
+ declare function isHTTP<SD>(context: BaseContext<SD>): context is HTTPContext<SD>;
34
+ declare function isWS<SD>(context: BaseContext<SD>): context is WSContext<SD>;
35
+ //#endregion
36
+ export { DeriveUpgradeValue as a, ExtractDeriveUpgradeData as c, WSContext as d, isHTTP as f, DeriveUpgradePossibleValue as i, HTTPContext as l, DeriveFunction as n, DeriveValue as o, isWS as p, DeriveUpgradeFunction as r, ExtractDerive as s, BaseContext as t, RebindSocketData as u };
@@ -1,2 +1,2 @@
1
- import { a as isHTTP, i as WSContext, n as DefaultSocketData, o as isWS, r as HTTPContext, t as BaseContext } from "./context-BZAzgugo.mjs";
2
- export { BaseContext, DefaultSocketData, HTTPContext, WSContext, isHTTP, isWS };
1
+ import { a as DeriveUpgradeValue, c as ExtractDeriveUpgradeData, d as WSContext, f as isHTTP, i as DeriveUpgradePossibleValue, l as HTTPContext, n as DeriveFunction, o as DeriveValue, p as isWS, r as DeriveUpgradeFunction, s as ExtractDerive, t as BaseContext, u as RebindSocketData } from "./context-iXF4Voqe.mjs";
2
+ export { BaseContext, DeriveFunction, DeriveUpgradeFunction, DeriveUpgradePossibleValue, DeriveUpgradeValue, DeriveValue, ExtractDerive, ExtractDeriveUpgradeData, HTTPContext, RebindSocketData, WSContext, isHTTP, isWS };
package/dist/cors.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { t as BaseContext } from "./context-BZAzgugo.mjs";
2
- import "./http-NhhQxMv8.mjs";
3
- import { t as Middleware } from "./middleware-DB4PWjb4.mjs";
1
+ import { t as BaseContext } from "./context-iXF4Voqe.mjs";
2
+ import "./http-AGiFONuJ.mjs";
3
+ import { t as Middleware } from "./middleware-CxmfdaxU.mjs";
4
4
 
5
5
  //#region src/cors.d.ts
6
6
  interface CorsConfig {
package/dist/cors.mjs CHANGED
@@ -1 +1 @@
1
- import{t as e}from"./response-D935Hx3o.mjs";const t={"Access-Control-Allow-Methods":[`GET`,`POST`,`PUT`,`PATCH`,`DELETE`,`OPTIONS`].join(`, `),"Access-Control-Allow-Headers":[`Accept-Language`,`Accept`,`Content-Type`,`Content-Language`,`Range`].join(`, `),"Access-Control-Expose-Headers":[`Cache-Control`,`Content-Language`,`Content-Type`,`Expires`,`Last-Modified`,`Pragma`].join(`, `),"Access-Control-Max-Age":`3600`};function n(n){let r={...t,...n.methods?.length&&{"Access-Control-Allow-Methods":n.methods.join(`, `)},...n.allowedHeaders?.length&&{"Access-Control-Allow-Headers":n.allowedHeaders.join(`, `)},...n.exposedHeaders?.length&&{"Access-Control-Expose-Headers":n.exposedHeaders.join(`, `)},...n.credentials&&!n.origin.includes(`*`)&&{"Access-Control-Allow-Credentials":`true`},...n.maxAge&&{"Access-Control-Max-Age":n.maxAge.toString()}};return async t=>{if(!(`request`in t.context))return t.next();let i=t.context.request.headers.get(`Origin`);if(!i||!n.origin.includes(i))return t.next();if(t.context.request.method===`OPTIONS`)return new Response(null,{status:204,headers:{"Access-Control-Allow-Origin":i,...r}});let a=e(await t.next());for(let e in a.headers.set(`Access-Control-Allow-Origin`,i),r)a.headers.set(e,r[e]);return a}}export{n as createCORSMiddleware};
1
+ import{t as e}from"./response-DiL4kmkY.mjs";const t={"Access-Control-Allow-Methods":[`GET`,`POST`,`PUT`,`PATCH`,`DELETE`,`OPTIONS`].join(`, `),"Access-Control-Allow-Headers":[`Accept-Language`,`Accept`,`Content-Type`,`Content-Language`,`Range`].join(`, `),"Access-Control-Expose-Headers":[`Cache-Control`,`Content-Language`,`Content-Type`,`Expires`,`Last-Modified`,`Pragma`].join(`, `),"Access-Control-Max-Age":`3600`};function n(n){let r={...t,...n.methods?.length&&{"Access-Control-Allow-Methods":n.methods.join(`, `)},...n.allowedHeaders?.length&&{"Access-Control-Allow-Headers":n.allowedHeaders.join(`, `)},...n.exposedHeaders?.length&&{"Access-Control-Expose-Headers":n.exposedHeaders.join(`, `)},...n.credentials&&!n.origin.includes(`*`)&&{"Access-Control-Allow-Credentials":`true`},...n.maxAge&&{"Access-Control-Max-Age":n.maxAge.toString()}};return async t=>{if(!(`request`in t.context))return t.next();let i=t.context.request.headers.get(`Origin`);if(!i||!n.origin.includes(i))return t.next();if(t.context.request.method===`OPTIONS`)return new Response(null,{status:204,headers:{"Access-Control-Allow-Origin":i,...r}});let a;try{a=e(await t.next())}catch(t){a=e(t)}for(let e in a.headers.set(`Access-Control-Allow-Origin`,i),r)a.headers.set(e,r[e]);return a}}export{n as createCORSMiddleware};
@@ -1,10 +1,9 @@
1
- import { n as DefaultSocketData } from "./context-BZAzgugo.mjs";
2
- import { o as Promisable } from "./types-Cn69QrjS.mjs";
1
+ import { o as Promisable } from "./types-DaFEaXF4.mjs";
3
2
  import { BunRequest, Server } from "bun";
4
3
 
5
4
  //#region src/http.d.ts
6
5
  type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD';
7
- type BunRouteHandler = ((request: BunRequest, server: Server<DefaultSocketData>) => Promisable<Response | undefined>) | Response;
6
+ type BunRouteHandler = (request: BunRequest, server: Server<unknown>) => Promisable<Response>;
8
7
  type BunRoutes = Record<string, Partial<Record<HTTPMethod, BunRouteHandler>> | BunRouteHandler>;
9
8
  //#endregion
10
9
  export { BunRoutes as n, HTTPMethod as r, BunRouteHandler as t };
package/dist/http.d.mts CHANGED
@@ -1,3 +1,2 @@
1
- import "./context-BZAzgugo.mjs";
2
- import { n as BunRoutes, r as HTTPMethod, t as BunRouteHandler } from "./http-NhhQxMv8.mjs";
1
+ import { n as BunRoutes, r as HTTPMethod, t as BunRouteHandler } from "./http-AGiFONuJ.mjs";
3
2
  export { BunRouteHandler, BunRoutes, HTTPMethod };
@@ -0,0 +1,53 @@
1
+ import { o as Promisable } from "./types-DaFEaXF4.mjs";
2
+ import { r as HTTPMethod } from "./http-AGiFONuJ.mjs";
3
+ import { c as StandardSchemaV1 } from "./validation-CkRfxQJ_.mjs";
4
+
5
+ //#region src/procedure.d.ts
6
+ interface ProcedureOptions<I, C> {
7
+ input: I;
8
+ context: C;
9
+ }
10
+ /** Input: I, Output: O, Context: C */
11
+ type ProcedureHandler<I, O, C> = (options: ProcedureOptions<I, C>) => Promisable<O>;
12
+ interface HTTPOptions {
13
+ enabled?: boolean;
14
+ method?: HTTPMethod;
15
+ }
16
+ declare class Procedure<I = unknown, O = unknown, C = unknown> {
17
+ protected readonly middlewares: Set<Middleware<I, O, C>>;
18
+ protected inputSchema?: StandardSchemaV1<I>;
19
+ protected outputSchema?: StandardSchemaV1<O>;
20
+ protected handlerFunction?: ProcedureHandler<I, O, C>;
21
+ protected httpOptions?: HTTPOptions;
22
+ /** Set procedure input validation schema or type */
23
+ input<const NI>(schema?: StandardSchemaV1<NI>): Procedure<NI, O, C>;
24
+ /** Set procedure output validation schema or type */
25
+ output<const NO>(schema?: StandardSchemaV1<NO>): Procedure<I, NO, C>;
26
+ /** Set procedure handler function */
27
+ handler<const ActualOutput>(handler: ProcedureHandler<I, unknown extends O ? ActualOutput : O, C>): Procedure<I, unknown extends O ? ActualOutput : O, C>;
28
+ /** Set HTTP options for the procedure */
29
+ http(options?: HTTPOptions | boolean | HTTPMethod): Procedure<I, O, C>;
30
+ /** Add middleware to the procedure */
31
+ use(middleware: Middleware<I, O, C>): this;
32
+ /** Returns a function that checks input and output parameters. */
33
+ compile(): ProcedureHandler<I, O, C>;
34
+ /** Get procedure metadata information */
35
+ metadata(): {
36
+ http: HTTPOptions | undefined;
37
+ middlewares: Set<Middleware<I, O, C>>;
38
+ has: {
39
+ handler: boolean;
40
+ middleware: boolean;
41
+ input: boolean;
42
+ output: boolean;
43
+ };
44
+ };
45
+ }
46
+ //#endregion
47
+ //#region src/middleware.d.ts
48
+ interface MiddlewareOptions<I, O, C> extends ProcedureOptions<I, C> {
49
+ next: () => ReturnType<ProcedureHandler<I, O, C>>;
50
+ }
51
+ type Middleware<I, O, C> = (options: MiddlewareOptions<I, O, C>) => ReturnType<ProcedureHandler<I, O, C>>;
52
+ //#endregion
53
+ export { ProcedureHandler as a, Procedure as i, MiddlewareOptions as n, ProcedureOptions as o, HTTPOptions as r, Middleware as t };
@@ -1,4 +1,3 @@
1
- import "./context-BZAzgugo.mjs";
2
- import "./http-NhhQxMv8.mjs";
3
- import { n as MiddlewareOptions, t as Middleware } from "./middleware-DB4PWjb4.mjs";
1
+ import "./http-AGiFONuJ.mjs";
2
+ import { n as MiddlewareOptions, t as Middleware } from "./middleware-CxmfdaxU.mjs";
4
3
  export { Middleware, MiddlewareOptions };
@@ -0,0 +1 @@
1
+ import{t as e}from"./validation-BY5LC99k.mjs";var t=class{middlewares=new Set;inputSchema;outputSchema;handlerFunction;httpOptions;input(e){return e&&(this.inputSchema=e),this}output(e){return e&&(this.outputSchema=e),this}handler(e){return this.handlerFunction=e,this}http(e){switch(typeof e){case`boolean`:this.httpOptions={enabled:e};break;case`string`:this.httpOptions={enabled:!0,method:e};break;default:this.httpOptions={enabled:!0,...e??{}}}return this}use(e){return this.middlewares.add(e),this}compile(){if(!this.handlerFunction)throw Error(`Procedure handler is not defined`);if(!this.inputSchema&&!this.outputSchema)return this.handlerFunction;let t=this.handlerFunction;switch(!0){case!this.inputSchema:{let n=t;t=async t=>e(this.outputSchema,await n(t));break}case!this.outputSchema:{let n=t;t=async t=>n({...t,input:await e(this.inputSchema,t.input)});break}default:{let n=t;t=async t=>{let r=await n({...t,input:await e(this.inputSchema,t.input)});return e(this.outputSchema,r)}}}return t}metadata(){return{http:this.httpOptions,middlewares:this.middlewares,has:{handler:!!this.handlerFunction,middleware:this.middlewares.size>0,input:!!this.inputSchema,output:!!this.outputSchema}}}};export{t};
@@ -1,4 +1,3 @@
1
- import "./context-BZAzgugo.mjs";
2
- import "./http-NhhQxMv8.mjs";
3
- import { a as ProcedureHandler, i as Procedure, o as ProcedureOptions, r as HTTPOptions } from "./middleware-DB4PWjb4.mjs";
1
+ import "./http-AGiFONuJ.mjs";
2
+ import { a as ProcedureHandler, i as Procedure, o as ProcedureOptions, r as HTTPOptions } from "./middleware-CxmfdaxU.mjs";
4
3
  export { HTTPOptions, Procedure, ProcedureHandler, ProcedureOptions };
@@ -1 +1 @@
1
- import{t as e}from"./procedure-AntXykJT.mjs";export{e as Procedure};
1
+ import{t as e}from"./procedure-DMThMtgF.mjs";export{e as Procedure};
@@ -0,0 +1 @@
1
+ import{n as e}from"./error-Dq1biCC8.mjs";function t(t){switch(!0){case t instanceof Response:return t;case t instanceof e:return t.toResponse();case t instanceof Error:return new Response(t.message,{status:500});case typeof t==`object`:return Response.json(t);case!t:return new Response(null,{status:204});default:return new Response(String(t))}}function n(t,n){let r;switch(!0){case n instanceof e:r={id:t,error:{code:n.status,message:n.message}};break;case n instanceof Error:r={id:t,error:{code:500,message:n.message}};break;case n instanceof Response:r={id:t,error:{code:n.status,message:n.statusText}};break;case typeof n==`object`||typeof n==`number`||typeof n==`boolean`:r={id:t,result:n};break;default:r={id:t,result:String(n)}}return JSON.stringify(r)}export{n,t};
package/dist/response.mjs CHANGED
@@ -1 +1 @@
1
- import{n as e,t}from"./response-D935Hx3o.mjs";export{t as toHTTPResponse,e as toRPCResponse};
1
+ import{n as e,t}from"./response-DiL4kmkY.mjs";export{t as toHTTPResponse,e as toRPCResponse};
package/dist/rpc.d.mts CHANGED
@@ -1,2 +1,23 @@
1
- import { a as isRPC, i as SuccessResult, n as Payload, r as Result, t as ErrorResult } from "./rpc-Ch2UXReT.mjs";
1
+ import { a as JSONValue } from "./types-DaFEaXF4.mjs";
2
+
3
+ //#region src/rpc.d.ts
4
+ interface ErrorResult {
5
+ id: string;
6
+ error: {
7
+ code: number;
8
+ message: string;
9
+ };
10
+ }
11
+ interface SuccessResult {
12
+ id: string;
13
+ result: JSONValue;
14
+ }
15
+ type Result = ErrorResult | SuccessResult;
16
+ interface Payload {
17
+ id: string;
18
+ method: string;
19
+ params?: JSONValue;
20
+ }
21
+ declare function isRPC(value: any): value is Payload;
22
+ //#endregion
2
23
  export { ErrorResult, Payload, Result, SuccessResult, isRPC };
@@ -1,8 +1,8 @@
1
- import { n as DefaultSocketData, t as BaseContext } from "./context-BZAzgugo.mjs";
2
- import { a as JSONValue, o as Promisable, r as JSONObject, t as DeepReadonly } from "./types-Cn69QrjS.mjs";
3
- import "./http-NhhQxMv8.mjs";
4
- import "./middleware-DB4PWjb4.mjs";
5
- import { n as Ultra, t as ProceduresMap } from "./ultra-Dawe5ndl.mjs";
1
+ import { a as JSONValue, o as Promisable, r as JSONObject, t as DeepReadonly } from "./types-DaFEaXF4.mjs";
2
+ import { d as WSContext, l as HTTPContext, t as BaseContext } from "./context-iXF4Voqe.mjs";
3
+ import "./http-AGiFONuJ.mjs";
4
+ import "./middleware-CxmfdaxU.mjs";
5
+ import { ProceduresMap, Ultra } from "./ultra.mjs";
6
6
  import { BunRequest, CookieSameSite, RedisClient } from "bun";
7
7
 
8
8
  //#region src/session.d.ts
@@ -54,11 +54,25 @@ type SessionContext = BaseContext<{
54
54
  };
55
55
  declare function defineConfig<S extends Record<string, SessionStoreFactory>>(config: SessionConfig<S>): SessionConfig<S>;
56
56
  /** Extends context and socket data, initiate session instance every request */
57
- declare function createSessionModule<S extends Record<string, SessionStoreFactory>>(config: SessionConfig<S>): Ultra<ProceduresMap, (BaseContext & {
57
+ declare function createSessionModule<S extends Record<string, SessionStoreFactory>>(config: SessionConfig<S>): Ultra<ProceduresMap, {
58
+ sessionId: string;
59
+ }, ((HTTPContext<{
60
+ sessionId: string;
61
+ }> & Omit<HTTPContext<{
62
+ sessionId: string;
63
+ }> & Omit<HTTPContext<unknown>, keyof HTTPContext<any>> & {
58
64
  session: Session<S>;
59
- }) & BaseContext, DefaultSocketData & {
65
+ }, keyof HTTPContext<any>>) | (WSContext<{
66
+ sessionId: string;
67
+ }> & Omit<WSContext<{
68
+ sessionId: string;
69
+ }> & Omit<WSContext<unknown>, keyof WSContext<any>> & {
70
+ session: Session<S>;
71
+ }, keyof WSContext<any>>)) & ((HTTPContext<{
72
+ sessionId: string;
73
+ }> & Omit<HTTPContext<unknown>, keyof HTTPContext<any>>) | (WSContext<{
60
74
  sessionId: string;
61
- }>;
75
+ }> & Omit<WSContext<unknown>, keyof WSContext<any>>))>;
62
76
  /** Stores the ID in a cookie, then moves it to the socket and uses it for requests */
63
77
  declare class Session<Stores extends Record<string, SessionStoreFactory> = Record<string, SessionStoreFactory>> {
64
78
  /** Make random session id */
@@ -102,10 +116,6 @@ declare class RedisSessionStore implements SessionStore {
102
116
  }
103
117
  declare class MemorySessionStore implements SessionStore {
104
118
  protected readonly config: SessionConfig<any>;
105
- protected readonly sessions: Map<string, {
106
- data: SessionData;
107
- touched: number;
108
- }>;
109
119
  protected readonly sweepIntervalMs: number;
110
120
  protected readonly ttlMs: number;
111
121
  protected lastSweepAt: number;
package/dist/session.mjs CHANGED
@@ -1 +1 @@
1
- import{i as e}from"./error-Dq1biCC8.mjs";import{isHTTP as t,isWS as n}from"./context.mjs";import{sign as r,unsign as i}from"./crypto.mjs";import{Ultra as a}from"./ultra.mjs";import{randomBytes as o}from"node:crypto";function s(e){return{...e,cookie:{path:`/`,httpOnly:!0,secure:!0,sameSite:`lax`,maxAge:e.ttlSec,...e?.cookie}}}function c(e){return new a().deriveWS(t=>({sessionId:l.getOrCreateId(t.request,e)})).derive(t=>({session:new l(e,t)})).use(async({context:e,next:t})=>{await e.session.initiate();let n=await t();return await e.session.commit(),n})}var l=class a{static makeId(){return o(16).toString(`base64url`)}static getOrCreateId(e,t){let n=e.cookies.get(t.name);return n&&i(n,t.secret)||a.makeId()}config;context;store;sessionIdFromClient=null;sessionId;sessionState=null;modified=!1;constructor(r,o){switch(this.config=r,this.context=o,this.store=r.stores[r.store](r,o),!0){case t(o):{let e=o.request.cookies.get(r.name);e&&(this.sessionIdFromClient=i(e,r.secret));break}case n(o):this.sessionIdFromClient=o.ws.data.sessionId||null;break;default:throw new e(`Session management is only supported for HTTP and WebSocket protocols.`)}this.sessionId=this.sessionIdFromClient||a.makeId()}get id(){return this.sessionId}async initiate(){this.sessionState||=await this.store.read(this.sessionId)||{}}async commit(){if(this.touch(),this.isEmpty&&this.sessionIdFromClient)return this.store.destroy(this.sessionIdFromClient);this.sessionIdFromClient&&this.sessionIdFromClient!==this.sessionId?(await this.store.destroy(this.sessionIdFromClient),await this.store.write(this.sessionId,this.state)):this.modified?await this.store.write(this.sessionId,this.state):await this.store.touch(this.sessionId)}regenerate(){this.sessionId=a.makeId()}get(e,t){return this.state[e]??t??null}set(e,t){this.state[e]=t,this.modified=!0}has(e){return Object.hasOwn(this.state,e)}all(){return this.state}delete(e){delete this.state[e],this.modified=!0}clear(){this.sessionState={},this.modified=!0}get state(){if(!this.sessionState)throw Error(`Session is not initiated yet.`);return this.sessionState}get isEmpty(){return Object.keys(this.state).length===0}touch(){`request`in this.context&&this.context.request.cookies.set(this.config.name,r(this.sessionId,this.config.secret),this.config.cookie)}},u=class{config;connection;constructor(e,t){this.config=e,this.connection=t}async read(e){let t=await this.connection.get(`${this.config.name}:${e}`);return t?JSON.parse(t):null}async write(e,t){await this.connection.set(`${this.config.name}:${e}`,JSON.stringify(t),`EX`,this.config.ttlSec)}async destroy(e){await this.connection.del(`${this.config.name}:${e}`)}async touch(e){await this.connection.expire(`${this.config.name}:${e}`,this.config.ttlSec)}},d=class{config;sessions=new Map;sweepIntervalMs;ttlMs;lastSweepAt=Date.now();constructor(e,t=e.ttlSec){this.config=e,this.sweepIntervalMs=t*1e3,this.ttlMs=e.ttlSec*1e3}read(e){return this.maybeSweep(),this.sessions.get(e)?.data||null}write(e,t){this.maybeSweep(),this.sessions.set(e,{data:t,touched:Date.now()})}destroy(e){this.maybeSweep(),this.sessions.delete(e)}touch(e){this.maybeSweep();let t=this.sessions.get(e);t&&(t.touched=Date.now())}maybeSweep(e=Date.now()){if(!(e-this.lastSweepAt<this.sweepIntervalMs)){this.lastSweepAt=e;for(let[t,n]of this.sessions)e-n.touched>this.ttlMs&&this.sessions.delete(t)}}};export{d as MemorySessionStore,u as RedisSessionStore,l as Session,c as createSessionModule,s as defineConfig};
1
+ import{i as e}from"./error-Dq1biCC8.mjs";import{Ultra as t}from"./ultra.mjs";import{isHTTP as n,isWS as r}from"./context.mjs";import{sign as i,unsign as a}from"./crypto.mjs";import{Cookie as o}from"bun";import{randomBytes as s}from"node:crypto";function c(e){return{...e,cookie:{path:`/`,httpOnly:!0,secure:!0,sameSite:`lax`,maxAge:e.ttlSec,...e?.cookie}}}function l(e){return new t().deriveUpgrade(t=>{let n=u.getOrCreateId(t.request,e);return{headers:{"Set-Cookie":new o(e.name,i(n,e.secret),e.cookie).toString()},data:{sessionId:n}}}).derive(t=>({session:new u(e,t)})).use(async({context:e,next:t})=>{await e.session.initiate();let n=await t();return await e.session.commit(),n})}var u=class t{static makeId(){return s(16).toString(`base64url`)}static getOrCreateId(e,n){let r=e.cookies.get(n.name);return r&&a(r,n.secret)||t.makeId()}config;context;store;sessionIdFromClient=null;sessionId;sessionState=null;modified=!1;constructor(i,o){switch(this.config=i,this.context=o,this.store=i.stores[i.store](i,o),!0){case n(o):{let e=o.request.cookies.get(i.name);e&&(this.sessionIdFromClient=a(e,i.secret));break}case r(o):this.sessionIdFromClient=o.ws.data.sessionId||null;break;default:throw new e(`Session management is only supported for HTTP and WebSocket protocols.`)}this.sessionId=this.sessionIdFromClient||t.makeId()}get id(){return this.sessionId}async initiate(){this.sessionState||=await this.store.read(this.sessionId)||{}}async commit(){if(this.touch(),this.isEmpty&&this.sessionIdFromClient)return this.store.destroy(this.sessionIdFromClient);this.sessionIdFromClient&&this.sessionIdFromClient!==this.sessionId?(await this.store.destroy(this.sessionIdFromClient),await this.store.write(this.sessionId,this.state)):this.modified?await this.store.write(this.sessionId,this.state):await this.store.touch(this.sessionId)}regenerate(){this.sessionId=t.makeId()}get(e,t){return this.state[e]??t??null}set(e,t){this.state[e]=t,this.modified=!0}has(e){return Object.hasOwn(this.state,e)}all(){return this.state}delete(e){delete this.state[e],this.modified=!0}clear(){this.sessionState={},this.modified=!0}get state(){if(!this.sessionState)throw Error(`Session is not initiated yet.`);return this.sessionState}get isEmpty(){return Object.keys(this.state).length===0}touch(){`request`in this.context&&this.context.request.cookies.set(this.config.name,i(this.sessionId,this.config.secret),this.config.cookie)}},d=class{config;connection;constructor(e,t){this.config=e,this.connection=t}async read(e){let t=await this.connection.get(`${this.config.name}:${e}`);return t?JSON.parse(t):null}async write(e,t){await this.connection.set(`${this.config.name}:${e}`,JSON.stringify(t),`EX`,this.config.ttlSec)}async destroy(e){await this.connection.del(`${this.config.name}:${e}`)}async touch(e){await this.connection.expire(`${this.config.name}:${e}`,this.config.ttlSec)}};const f=new Map;var p=class{config;sweepIntervalMs;ttlMs;lastSweepAt=Date.now();constructor(e,t=e.ttlSec){this.config=e,this.sweepIntervalMs=t*1e3,this.ttlMs=e.ttlSec*1e3}read(e){return this.maybeSweep(),f.get(e)?.data??null}write(e,t){this.maybeSweep(),f.set(e,{data:t,touched:Date.now()})}destroy(e){this.maybeSweep(),f.delete(e)}touch(e){this.maybeSweep();let t=f.get(e);t&&(t.touched=Date.now())}maybeSweep(e=Date.now()){if(!(e-this.lastSweepAt<this.sweepIntervalMs)){this.lastSweepAt=e;for(let[t,n]of f)e-n.touched>this.ttlMs&&f.delete(t)}}};export{p as MemorySessionStore,d as RedisSessionStore,u as Session,l as createSessionModule,c as defineConfig};
package/dist/types.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as JSONValue, i as JSONPrimitive, n as JSONArray, o as Promisable, r as JSONObject, t as DeepReadonly } from "./types-Cn69QrjS.mjs";
1
+ import { a as JSONValue, i as JSONPrimitive, n as JSONArray, o as Promisable, r as JSONObject, t as DeepReadonly } from "./types-DaFEaXF4.mjs";
2
2
  export { DeepReadonly, JSONArray, JSONObject, JSONPrimitive, JSONValue, Promisable };
package/dist/ultra.d.mts CHANGED
@@ -1,5 +1,72 @@
1
- import "./context-BZAzgugo.mjs";
2
- import "./http-NhhQxMv8.mjs";
3
- import "./middleware-DB4PWjb4.mjs";
4
- import { n as Ultra, t as ProceduresMap } from "./ultra-Dawe5ndl.mjs";
5
- export { ProceduresMap, Ultra };
1
+ import { a as DeriveUpgradeValue, c as ExtractDeriveUpgradeData, o as DeriveValue, s as ExtractDerive, t as BaseContext, u as RebindSocketData } from "./context-iXF4Voqe.mjs";
2
+ import { n as BunRoutes } from "./http-AGiFONuJ.mjs";
3
+ import { c as StandardSchemaV1 } from "./validation-CkRfxQJ_.mjs";
4
+ import { a as ProcedureHandler, i as Procedure, t as Middleware } from "./middleware-CxmfdaxU.mjs";
5
+ import { BunRequest, ErrorLike, Server, ServerWebSocket } from "bun";
6
+
7
+ //#region src/ultra.d.ts
8
+ interface ProceduresMap {
9
+ [key: string]: Procedure<any, any, any> | ProceduresMap;
10
+ }
11
+ interface ServerEventMap<SD> {
12
+ 'error': [ErrorLike];
13
+ 'unhandled:error': [ErrorLike];
14
+ 'http:request': [BunRequest, Server<SD>];
15
+ 'ws:open': [ServerWebSocket<SD>];
16
+ 'ws:message': [ServerWebSocket<SD>, string | Buffer<ArrayBuffer>];
17
+ 'ws:close': [ServerWebSocket<SD>, number, string];
18
+ 'server:started': [Server<SD>];
19
+ 'server:stopped': [Server<SD>, closeActiveConnections: boolean];
20
+ }
21
+ type ServerEventListener<SD, K extends keyof ServerEventMap<SD>> = (...args: ServerEventMap<SD>[K]) => any;
22
+ type StartOptions<SD> = Partial<Bun.Serve.Options<SD>>;
23
+ type InputFactory<C> = <I>(schema?: StandardSchemaV1<I>) => Procedure<I, unknown, C>;
24
+ type ProcedureMapInitializer<R extends ProceduresMap, C> = (input: InputFactory<C>) => R;
25
+ interface UltraOptions {
26
+ http?: {
27
+ enableByDefault?: boolean;
28
+ };
29
+ }
30
+ declare class Ultra<Procedures extends ProceduresMap = ProceduresMap, SocketData = unknown, Context extends BaseContext<SocketData> = BaseContext<SocketData>> {
31
+ protected readonly initializers: Map<ProcedureMapInitializer<any, Context>, Set<Middleware<any, any, Context>>>;
32
+ protected readonly events: Map<keyof ServerEventMap<SocketData>, Set<ServerEventListener<SocketData, any>>>;
33
+ protected readonly middlewares: Set<Middleware<any, any, Context>>;
34
+ protected readonly derived: Set<DeriveValue<Context>>;
35
+ protected readonly derivedUpgrade: Set<DeriveUpgradeValue<Context>>;
36
+ protected readonly options: UltraOptions;
37
+ protected httpEnabled: boolean;
38
+ protected server?: Server<SocketData>;
39
+ constructor(options?: UltraOptions);
40
+ /** Register procedures */
41
+ routes<const P extends ProceduresMap>(initializer: ProcedureMapInitializer<P, Context>, middlewares?: Middleware<any, any, Context>[]): Ultra<Procedures & P, SocketData, Context>;
42
+ /** Register middleware or another Ultra instance */
43
+ use<const PluginProcedures extends ProceduresMap, const PluginSocketData, const PluginContext extends BaseContext<PluginSocketData>>(entity: Middleware<any, any, Context> | Ultra<PluginProcedures, PluginSocketData, PluginContext>): Ultra<Procedures & PluginProcedures, SocketData & PluginSocketData, RebindSocketData<Context, SocketData & PluginSocketData> & RebindSocketData<PluginContext, SocketData & PluginSocketData>>;
44
+ /** Extends context values for every request with provided values */
45
+ derive<const D extends DeriveValue<Context>>(derive: D): Ultra<Procedures, SocketData, Context & ExtractDerive<Context, D>>;
46
+ /** Extends socket data and return headers */
47
+ deriveUpgrade<const D extends DeriveUpgradeValue<Context>>(derive: D): Ultra<Procedures, SocketData & ExtractDeriveUpgradeData<Context, D>, RebindSocketData<Context, SocketData & ExtractDeriveUpgradeData<Context, D>>>;
48
+ /** Build procedures and start servers */
49
+ start(options?: StartOptions<SocketData>): Server<SocketData>;
50
+ /** Stop server */
51
+ stop(closeActiveConnections?: boolean): Promise<void>;
52
+ on<E extends keyof ServerEventMap<SocketData>>(event: E, listener: ServerEventListener<SocketData, E>): this;
53
+ off<E extends keyof ServerEventMap<SocketData>>(event: E, listener: ServerEventListener<SocketData, E>): this;
54
+ emit<E extends keyof ServerEventMap<SocketData>>(event: E, ...args: ServerEventMap<SocketData>[E]): this;
55
+ /** Enrich context with derived values */
56
+ protected enrichContext(context: BaseContext<SocketData>): Promise<Context>;
57
+ /** Enrich upgrade options with derived values */
58
+ protected enrichUpgrade(context: Context): Promise<{
59
+ data: Record<PropertyKey, any>;
60
+ headers: Headers;
61
+ }>;
62
+ /** Merge other Ultra instance with deduplication */
63
+ protected merge(module: Ultra<any, any, any>): void;
64
+ /** Wrap procedure handler with global middlewares */
65
+ protected wrapHandler<I, O>(handler: ProcedureHandler<I, O, Context>, middlewares: Set<Middleware<I, O, Context>>): ProcedureHandler<I, any, Context>;
66
+ protected build(): {
67
+ handlers: Map<string, ProcedureHandler<any, any, Context>>;
68
+ routes: BunRoutes;
69
+ };
70
+ }
71
+ //#endregion
72
+ export { InputFactory, ProcedureMapInitializer, ProceduresMap, Ultra, UltraOptions };
package/dist/ultra.mjs CHANGED
@@ -1 +1 @@
1
- import{n as e,t}from"./response-D935Hx3o.mjs";import{t as n}from"./procedure-AntXykJT.mjs";import{t as r}from"./rpc-CAwkzxZe.mjs";import{serve as i}from"bun";var a=class{initializers=new Set;handlers=new Map;events=new Map;middlewares=new Set;derived=new Set;derivedWS=new Set;options={http:!0};server;constructor(e){Object.assign(this.options,e)}routes(e){return this.initializers.add(e),this}use(e){return typeof e==`function`?(this.middlewares.add(e),this):(this.merge(e),this)}derive(e){return this.derived.add(e),this}deriveWS(e){return this.derivedWS.add(e),this}start(e){if(this.server)return console.warn(`Server is already running`),this.server;let t=this.buildProcedures(),n=this.wrapHandler(()=>new Response(`Not Found`,{status:404}));return this.server=i({...e??{},routes:{...this.options.http&&{...this.buildRoutes(t),"/*":async(e,t)=>(this.emit(`http:request`,e,t),n({input:null,context:this.derived.size?await this.enrichContext({server:t,request:e}):{server:t,request:e}}))},"/ws":async(e,t)=>{if(this.derivedWS.size){let n={},r=this.derived.size?await this.enrichContext({server:t,request:e}):{server:t,request:e};for(let e of this.derivedWS)Object.assign(n,typeof e==`function`?await e(r):e);t.upgrade(e,{data:n});return}t.upgrade(e)}},websocket:{open:e=>{this.emit(`ws:open`,e)},close:(e,t,n)=>{this.emit(`ws:close`,e,t,n)},message:(e,t)=>{if(this.emit(`ws:message`,e,t),typeof t!=`string`)return;let n=JSON.parse(t);r(n)&&this.handleRPC(e,n)}},error:e=>(this.emit(`unhandled:error`,e),console.error(`Unhandled server error:`,e),new Response(`Internal Server Error`,{status:500}))}),this.emit(`server:started`,this.server),this.server}async stop(e=!1){if(!this.server)return console.error(`Server is not running`);await this.server.stop(e),this.emit(`server:stopped`,this.server,e)}on(e,t){return this.events.has(e)||this.events.set(e,new Set),this.events.get(e).add(t),this}off(e,t){return this.events.get(e)?.delete(t),this}emit(e,...t){return this.events.get(e)?.forEach(e=>e(...t)),this}async handleRPC(t,n){let r=this.handlers.get(n.method);if(!r){t.send(`{"id": "${n.id}", "error": {"code": 404, "message": "Not found"}}`);return}try{t.send(e(n.id,await r({input:n.params,context:await this.enrichContext({server:this.server,ws:t})})))}catch(r){this.emit(`error`,r),t.send(e(n.id,r))}}async enrichContext(e){for(let t of this.derived)Object.assign(e,typeof t==`function`?await t(e):t);return e}merge(e){e.initializers.forEach(e=>this.initializers.add(e)),e.derived.forEach(e=>this.derived.add(e)),e.derivedWS.forEach(e=>this.derivedWS.add(e)),e.middlewares.forEach(e=>this.middlewares.add(e)),e.events.forEach((e,t)=>{this.events.has(t)||this.events.set(t,new Set);let n=this.events.get(t);e.forEach(e=>n.add(e))})}wrapHandler(e){if(!this.middlewares.size)return e;let t=Array.from(this.middlewares);return async n=>{let r=0,i=()=>r===t.length?e(n):t[r++]({...n,next:i});return i()}}buildProcedures(){let e=new Map,t=e=>{let t=new n;return e?t.input(e):t};for(let r of this.initializers){let i=r(t),a=[];for(let[e,t]of Object.entries(i))a.push({path:e,value:t});for(;a.length;){let{path:t,value:r}=a.pop();if(r instanceof n){if(e.has(t))throw Error(`Procedure conflict at path "${t}"`);e.set(t,r),this.handlers.set(t,this.wrapHandler(r.wrap()));continue}for(let[e,n]of Object.entries(r)){let r=t?`${t}/${e}`:e;a.push({path:r,value:n})}}}return e}buildRoutes(e){let n={};for(let[r,i]of e){let e=i.getInfo();if(!e.http?.enabled)continue;let a=`/${r}`,o=this.handlers.get(r);if(!o)throw Error(`Handler for procedure at path "${r}" is not defined`);let s=async(n,i)=>{this.emit(`http:request`,n,i);let a=n.body,s={server:i,request:n},c=this.derived.size?await this.enrichContext(s):s;if(a&&e.hasInput){if(n.method===`GET`){let e=n.url.indexOf(`?`);e!==-1&&e<n.url.length-1&&(a=Object.fromEntries(new URLSearchParams(n.url.slice(e+1)).entries()))}else if(n.headers.get(`Content-Length`)!==`0`){let e=n.headers.get(`Content-Type`);if(e)switch(!0){case e.startsWith(`application/json`):a=await n.json();break;case e.startsWith(`text`):a=await n.text();break;case e.startsWith(`multipart/form-data`):a=await n.formData();break;default:console.error(`Unsupported Content-Type for procedure ${r}: ${e}`);break}}}try{return t(await o({input:a,context:c}))}catch(e){return this.emit(`error`,e),t(e)}};if(!e.http.method){n[a]=s;continue}n[a]||(n[a]={}),n[a][e.http.method]=s}return n}};export{a as Ultra};
1
+ import{t as e}from"./procedure-DMThMtgF.mjs";import{n as t,t as n}from"./response-DiL4kmkY.mjs";import{t as r}from"./rpc-CAwkzxZe.mjs";import{serve as i}from"bun";var a=class{initializers=new Map;events=new Map;middlewares=new Set;derived=new Set;derivedUpgrade=new Set;options={http:{enableByDefault:!1}};httpEnabled=!1;server;constructor(e){e&&(this.options={...this.options,...e}),this.httpEnabled=this.options.http?.enableByDefault??!1}routes(e,t){let n=this.initializers.get(e);return n?t?.forEach(e=>n.add(e)):this.initializers.set(e,new Set(t)),this}use(e){return typeof e==`function`?(this.middlewares.add(e),this):(this.merge(e),this)}derive(e){return this.derived.add(e),this}deriveUpgrade(e){return this.derivedUpgrade.add(e),this}start(e){if(this.server)return console.warn(`Server is already running`),this.server;let{routes:n,handlers:a}=this.build(),o=this.wrapHandler(()=>new Response(`Not Found`,{status:404}),this.middlewares);return this.server=i({...this.httpEnabled&&{routes:{...n,"/ws":async(e,t)=>{if(this.emit(`http:request`,e,t),!this.derivedUpgrade.size)return t.upgrade(e)?void 0:new Response(`WebSocket upgrade failed`,{status:500});let n=this.derived.size?await this.enrichContext({server:t,request:e}):{server:t,request:e};if(!t.upgrade(e,await this.enrichUpgrade(n)))return new Response(`WebSocket upgrade failed`,{status:500})},"/*":async(e,t)=>(this.emit(`http:request`,e,t),o({input:null,context:this.derived.size?await this.enrichContext({server:t,request:e}):{server:t,request:e}}))}},websocket:{data:{},open:e=>{this.emit(`ws:open`,e)},close:(e,t,n)=>{this.emit(`ws:close`,e,t,n)},message:async(e,n)=>{if(this.emit(`ws:message`,e,n),typeof n!=`string`)return;let i=JSON.parse(n);if(!r(i))return;let o=a.get(i.method);if(!o){e.send(`{"id": "${i.id}", "error": {"code": 404, "message": "Not found"}}`);return}try{e.send(t(i.id,await o({input:i.params,context:this.derived.size?await this.enrichContext({server:this.server,ws:e}):{server:this.server,ws:e}})))}catch(n){this.emit(`error`,n),e.send(t(i.id,n))}}},error:e=>(this.emit(`unhandled:error`,e),console.error(`Unhandled server error:`,e),new Response(`Internal server error`,{status:500})),...e}),this.emit(`server:started`,this.server),this.server}async stop(e=!1){if(!this.server)return console.error(`Server is not running`);await this.server.stop(e),this.emit(`server:stopped`,this.server,e)}on(e,t){return this.events.has(e)||this.events.set(e,new Set),this.events.get(e).add(t),this}off(e,t){return this.events.get(e)?.delete(t),this}emit(e,...t){return this.events.get(e)?.forEach(e=>e(...t)),this}async enrichContext(e){for(let t of this.derived)Object.assign(e,typeof t==`function`?await t(e):t);return e}async enrichUpgrade(e){let t={data:{},headers:new Headers};for(let n of this.derivedUpgrade){let r=typeof n==`function`?await n(e):n;if(`data`in r&&Object.assign(t.data,r.data),`headers`in r)for(let[e,n]of Object.entries(r.headers))t.headers.set(e,n)}return t}merge(e){for(let[t,n]of e.initializers){let e=this.initializers.get(t);e?n.forEach(t=>e.add(t)):this.initializers.set(t,new Set(n))}e.derived.forEach(e=>this.derived.add(e)),e.derivedUpgrade.forEach(e=>this.derivedUpgrade.add(e)),e.middlewares.forEach(e=>this.middlewares.add(e)),e.events.forEach((e,t)=>{this.events.has(t)||this.events.set(t,new Set);let n=this.events.get(t);e.forEach(e=>n.add(e))})}wrapHandler(e,t){if(!t.size)return e;let n=Array.from(t),r=n.length;return async t=>{let i=0,a=()=>i===r?e(t):n[i++]({...t,next:a});return a()}}build(){let t=new Map,r={},i=t=>{let n=new e;return t&&n.input(t),this.options.http?.enableByDefault&&n.http(),n};for(let[a,o]of this.initializers){let s=a(i),c=[];for(let e in s)c.push({path:e,value:s[e]});for(;c.length;){let{path:i,value:a}=c.pop();if(a instanceof e){if(t.has(i))throw Error(`Procedure "${i}" already exists`);let e=a.metadata();!this.httpEnabled&&e.http?.enabled&&(this.httpEnabled=!0);let s=new Set([...this.middlewares,...o,...e.middlewares]),c=this.wrapHandler(a.compile(),s);if(t.set(i,c),!e.http?.enabled)continue;let l=`/${i}`,u=async(e,t)=>{this.emit(`http:request`,e,t);let r=e.body;if(r){if(e.method===`GET`){let t=e.url.indexOf(`?`);t!==-1&&t<e.url.length-1&&(r=Object.fromEntries(new URLSearchParams(e.url.slice(t+1)).entries()))}else if(e.headers.get(`Content-Length`)!==`0`){let t=e.headers.get(`Content-Type`);if(t)switch(!0){case t.startsWith(`application/json`):r=await e.json();break;case t.startsWith(`text`):r=await e.text();break;case t.startsWith(`multipart/form-data`):r=await e.formData();break;default:console.error(`Unsupported Content-Type for procedure ${i}: ${t}`);break}}}try{return n(await c({input:r,context:this.derived.size?await this.enrichContext({server:t,request:e}):{server:t,request:e}}))}catch(e){return this.emit(`error`,e),n(e)}};if(!e.http.method){r[l]=u;continue}r[l]||(r[l]={}),r[l][e.http.method]=u;continue}for(let[e,t]of Object.entries(a)){let n=i?`${i}/${e}`:e;c.push({path:n,value:t})}}}return{handlers:t,routes:r}}};export{a as Ultra};
@@ -1 +1 @@
1
- import{t as e}from"./validation-dJpfcBud.mjs";export{e as validate};
1
+ import{t as e}from"./validation-BY5LC99k.mjs";export{e as validate};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sx3/ultra",
3
3
  "type": "module",
4
- "version": "0.0.4",
4
+ "version": "0.1.1",
5
5
  "private": false,
6
6
  "description": "Type-safe RPC over HTTP/WebSocket for Bun",
7
7
  "author": "SX3",
@@ -45,7 +45,7 @@
45
45
  "scripts": {
46
46
  "build": "tsdown",
47
47
  "dev": "tsdown --watch",
48
- "test": "bun test",
48
+ "test": "bun test && bunx tsc --noEmit",
49
49
  "lint": "eslint ."
50
50
  },
51
51
  "peerDependencies": {
@@ -1,17 +0,0 @@
1
- import { BunRequest, Server, ServerWebSocket } from "bun";
2
-
3
- //#region src/context.d.ts
4
- type DefaultSocketData = Record<string, any>;
5
- interface HTTPContext {
6
- server: Server<DefaultSocketData>;
7
- request: BunRequest;
8
- }
9
- interface WSContext<SocketData> {
10
- server: Server<SocketData>;
11
- ws: ServerWebSocket<SocketData>;
12
- }
13
- type BaseContext<SocketData extends DefaultSocketData = any> = HTTPContext | WSContext<SocketData>;
14
- declare function isHTTP(context: BaseContext): context is HTTPContext;
15
- declare function isWS<SocketData extends DefaultSocketData = DefaultSocketData>(context: BaseContext): context is WSContext<SocketData>;
16
- //#endregion
17
- export { isHTTP as a, WSContext as i, DefaultSocketData as n, isWS as o, HTTPContext as r, BaseContext as t };
@@ -1,43 +0,0 @@
1
- import { t as BaseContext } from "./context-BZAzgugo.mjs";
2
- import { o as Promisable } from "./types-Cn69QrjS.mjs";
3
- import { r as HTTPMethod } from "./http-NhhQxMv8.mjs";
4
- import { c as StandardSchemaV1 } from "./validation-CkRfxQJ_.mjs";
5
-
6
- //#region src/procedure.d.ts
7
- interface ProcedureOptions<I, C> {
8
- input: I;
9
- context: C;
10
- }
11
- type ProcedureHandler<I, O, C extends BaseContext> = (options: ProcedureOptions<I, C>) => Promisable<O>;
12
- interface HTTPOptions {
13
- enabled?: boolean;
14
- method?: HTTPMethod;
15
- }
16
- declare class Procedure<I = any, O = any, C extends BaseContext = BaseContext> {
17
- protected inputSchema?: StandardSchemaV1<I>;
18
- protected outputSchema?: StandardSchemaV1<O>;
19
- protected handlerFunction?: ProcedureHandler<I, O, C>;
20
- protected middleware: Set<Middleware<I, O, C>>;
21
- protected httpOptions?: HTTPOptions;
22
- input<const NI>(schema?: StandardSchemaV1<NI>): Procedure<NI, O, C>;
23
- output<const NO>(schema?: StandardSchemaV1<NO>): Procedure<I, NO, C>;
24
- handler<const ActualOutput>(handler: ProcedureHandler<I, ActualOutput, C>): Procedure<I, ActualOutput, C>;
25
- http(options?: {
26
- method?: HTTPMethod;
27
- }): Procedure<I, O, C>;
28
- use(middleware: Middleware<I, O, C>): Procedure<I, O, C>;
29
- wrap(): ProcedureHandler<I, O, C>;
30
- getInfo(): {
31
- http: HTTPOptions | undefined;
32
- hasInput: boolean;
33
- hasOutput: boolean;
34
- };
35
- }
36
- //#endregion
37
- //#region src/middleware.d.ts
38
- interface MiddlewareOptions<I, O, C extends BaseContext = BaseContext> extends ProcedureOptions<I, C> {
39
- next: () => ReturnType<ProcedureHandler<I, O, C>>;
40
- }
41
- type Middleware<I, O, C extends BaseContext> = (options: MiddlewareOptions<I, O, C>) => Awaited<ReturnType<ProcedureHandler<I, O, C>>>;
42
- //#endregion
43
- export { ProcedureHandler as a, Procedure as i, MiddlewareOptions as n, ProcedureOptions as o, HTTPOptions as r, Middleware as t };
@@ -1 +0,0 @@
1
- import{t as e}from"./validation-dJpfcBud.mjs";var t=class{inputSchema;outputSchema;handlerFunction;middleware=new Set;httpOptions;input(e){return e&&(this.inputSchema=e),this}output(e){return e&&(this.outputSchema=e),this}handler(e){return this.handlerFunction=e,this}http(e){return this.httpOptions={...e,enabled:!0},this}use(e){return this.middleware.add(e),this}wrap(){if(!this.handlerFunction)throw Error(`Procedure handler is not defined`);if(!this.inputSchema&&!this.outputSchema&&!this.middleware.size)return this.handlerFunction;let t=this.handlerFunction;switch(!0){case!this.inputSchema&&!this.outputSchema:break;case!this.inputSchema:{let n=t;t=async t=>e(this.outputSchema,await n(t));break}case!this.outputSchema:{let n=t;t=async t=>n({...t,input:await e(this.inputSchema,t.input)});break}default:{let n=t;t=async t=>{let r=await n({...t,input:await e(this.inputSchema,t.input)});return e(this.outputSchema,r)}}}if(this.middleware.size){let e=Array.from(this.middleware);for(let n=e.length-1;n>=0;n--){let r=e[n],i=t;t=e=>r({...e,next:()=>i(e)})}}return t}getInfo(){return{http:this.httpOptions,hasInput:!!this.inputSchema,hasOutput:!!this.outputSchema}}};export{t};
@@ -1 +0,0 @@
1
- import{n as e}from"./error-Dq1biCC8.mjs";function t(t){switch(!0){case t instanceof Response:return t;case t instanceof e:return t.toResponse();case t instanceof Error:return new Response(`Internal Server Error`,{status:500});case typeof t==`object`:return Response.json(t);case!t:return new Response(null,{status:204});default:return new Response(String(t))}}function n(t,n){let r;switch(!0){case n instanceof e:r={id:t,error:{code:n.status,message:n.message}};break;case n instanceof Error:r={id:t,error:{code:500,message:n.message}};break;case n instanceof Response:r={id:t,error:{code:n.status,message:n.statusText}};break;case typeof n==`object`||typeof n==`number`||typeof n==`boolean`:r={id:t,result:n};break;default:r={id:t,result:String(n)}}return JSON.stringify(r)}export{n,t};
@@ -1,23 +0,0 @@
1
- import { a as JSONValue } from "./types-Cn69QrjS.mjs";
2
-
3
- //#region src/rpc.d.ts
4
- interface ErrorResult {
5
- id: string;
6
- error: {
7
- code: number;
8
- message: string;
9
- };
10
- }
11
- interface SuccessResult {
12
- id: string;
13
- result: JSONValue;
14
- }
15
- type Result = ErrorResult | SuccessResult;
16
- interface Payload {
17
- id: string;
18
- method: string;
19
- params?: JSONValue;
20
- }
21
- declare function isRPC(value: any): value is Payload;
22
- //#endregion
23
- export { isRPC as a, SuccessResult as i, Payload as n, Result as r, ErrorResult as t };
@@ -1,69 +0,0 @@
1
- import { n as DefaultSocketData, t as BaseContext } from "./context-BZAzgugo.mjs";
2
- import { o as Promisable } from "./types-Cn69QrjS.mjs";
3
- import { n as BunRoutes } from "./http-NhhQxMv8.mjs";
4
- import { c as StandardSchemaV1 } from "./validation-CkRfxQJ_.mjs";
5
- import { a as ProcedureHandler, i as Procedure, t as Middleware } from "./middleware-DB4PWjb4.mjs";
6
- import { n as Payload } from "./rpc-Ch2UXReT.mjs";
7
- import { BunRequest, ErrorLike, Server, ServerWebSocket } from "bun";
8
-
9
- //#region src/ultra.d.ts
10
- interface ProceduresMap {
11
- [key: string]: Procedure<any, any, any> | ProceduresMap;
12
- }
13
- interface ServerEventMap<SocketData extends DefaultSocketData = DefaultSocketData> {
14
- 'error': [ErrorLike];
15
- 'unhandled:error': [ErrorLike];
16
- 'http:request': [BunRequest, Server<SocketData>];
17
- 'ws:open': [ServerWebSocket<SocketData>];
18
- 'ws:message': [ServerWebSocket<SocketData>, string | Buffer<ArrayBuffer>];
19
- 'ws:close': [ServerWebSocket<SocketData>, number, string];
20
- 'server:started': [Server<SocketData>];
21
- 'server:stopped': [Server<SocketData>, closeActiveConnections: boolean];
22
- }
23
- type ServerEventListener<K extends keyof ServerEventMap> = (...args: ServerEventMap[K]) => any;
24
- type DeriveFunction<C extends BaseContext> = (context: C) => Promisable<Record<PropertyKey, any>>;
25
- type DeriveValue<C extends BaseContext> = DeriveFunction<C> | Record<PropertyKey, any>;
26
- type ExtractDerive<C extends BaseContext, T extends DeriveValue<C>> = T extends DeriveFunction<C> ? Awaited<ReturnType<T>> : T;
27
- type StartOptions<SocketData extends DefaultSocketData> = Omit<Partial<Bun.Serve.Options<SocketData>>, 'websocket' | 'error' | 'routes'>;
28
- interface UltraOptions {
29
- http: boolean;
30
- }
31
- type InputFactory<C extends BaseContext> = <I>(schema?: StandardSchemaV1<I>) => Procedure<I, unknown, C>;
32
- type ProcedureMapInitializer<R extends ProceduresMap, C extends BaseContext = BaseContext> = (input: InputFactory<C>) => R;
33
- declare class Ultra<Procedures extends ProceduresMap = ProceduresMap, Context extends BaseContext = BaseContext, SocketData extends DefaultSocketData = DefaultSocketData> {
34
- protected readonly initializers: Set<ProcedureMapInitializer<any, Context>>;
35
- protected readonly handlers: Map<string, ProcedureHandler<any, any, Context>>;
36
- protected readonly events: Map<keyof ServerEventMap<SocketData>, Set<ServerEventListener<any>>>;
37
- protected readonly middlewares: Set<Middleware<any, any, Context>>;
38
- protected readonly derived: Set<DeriveValue<Context>>;
39
- protected readonly derivedWS: Set<DeriveValue<Context>>;
40
- protected readonly options: UltraOptions;
41
- protected server?: Server<SocketData>;
42
- constructor(options?: Partial<UltraOptions>);
43
- /** Register procedures */
44
- routes<const P extends ProceduresMap>(initializer: ProcedureMapInitializer<P, Context>): Ultra<Procedures & P, Context, SocketData>;
45
- /** Register middleware or another Ultra instance */
46
- use<const PluginProcedures extends ProceduresMap, const PluginContext extends BaseContext, const PluginSocketData extends DefaultSocketData>(entity: Middleware<any, any, Context> | Ultra<PluginProcedures, PluginContext, PluginSocketData>): Ultra<Procedures & PluginProcedures, Context & PluginContext, SocketData & PluginSocketData>;
47
- /** Extends context values for every request with provided values */
48
- derive<const D extends DeriveValue<Context>>(derive: D): Ultra<Procedures, Context & ExtractDerive<Context, D>, SocketData>;
49
- /** Extends WS data for every ws connection */
50
- deriveWS<const D extends DeriveValue<Context>>(derive: D): Ultra<Procedures, Context, SocketData & ExtractDerive<Context, D>>;
51
- start(options?: StartOptions<SocketData>): Server<SocketData>;
52
- stop(closeActiveConnections?: boolean): Promise<void>;
53
- on<E extends keyof ServerEventMap>(event: E, listener: ServerEventListener<E>): this;
54
- off<E extends keyof ServerEventMap>(event: E, listener: ServerEventListener<E>): this;
55
- emit<E extends keyof ServerEventMap>(event: E, ...args: ServerEventMap[E]): this;
56
- protected handleRPC(ws: ServerWebSocket<SocketData>, payload: Payload): Promise<void>;
57
- /** Enrich context with derived values */
58
- protected enrichContext(context: BaseContext): Promise<Context>;
59
- /** Merge other Ultra instance with deduplication */
60
- protected merge(module: Ultra<any, any, any>): void;
61
- /** Wrap procedure handler with global middlewares */
62
- protected wrapHandler(handler: ProcedureHandler<any, any, any>): ProcedureHandler<any, any, any>;
63
- /** Build flat map from procedures tree and write handles map */
64
- protected buildProcedures(): Map<string, Procedure<any, any, Context>>;
65
- /** Build Bun native HTTP routes */
66
- protected buildRoutes(procedures: Map<string, Procedure<any, any, Context>>): BunRoutes;
67
- }
68
- //#endregion
69
- export { Ultra as n, ProceduresMap as t };