@elysiajs/eden 0.3.0-exp-230223.1133 → 0.3.0-rc.0

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/src/types.ts CHANGED
@@ -1,203 +1,18 @@
1
1
  import type {
2
2
  Elysia,
3
3
  SCHEMA,
4
- TypedRoute,
5
4
  IsPathParameter,
6
- EXPOSED
5
+ EXPOSED,
6
+ AnyTypedSchema
7
7
  } from 'elysia'
8
- import type { TObject } from '@sinclair/typebox'
9
8
 
10
- import type { EdenWS } from '.'
11
- import { type EdenFetchError, type Signal } from './utils'
9
+ import type { EdenFetchError } from './utils'
12
10
 
13
- type IsAny<T> = unknown extends T
14
- ? [T] extends [object]
15
- ? true
16
- : false
17
- : false
18
-
19
- type Promisify<T extends (...args: any[]) => any> = T extends (
20
- ...args: infer Args
21
- ) => infer Return
22
- ? Return extends Promise<any>
23
- ? T
24
- : (...args: Args) => Promise<Return>
25
- : never
26
-
27
- type Asynctify<T> = T extends infer Fn extends (...args: any) => any
28
- ? Promisify<Fn>
29
- : T extends Record<string, any>
30
- ? {
31
- [K in keyof T]: EdenFn<T[K]>
32
- }
33
- : never
34
-
35
- type EdenFn<T> = T extends {
36
- [EXPOSED]: any
37
- value: infer Value
38
- }
39
- ? Asynctify<Value>
40
- : Asynctify<T>
41
-
42
- type CreateEdenFn<Exposed extends Record<string, any>> = EdenFn<Exposed> & {
43
- $set(config: EdenConfig): void
44
- $clone(config?: EdenConfig): CreateEdenFn<Exposed>
45
- }
46
-
47
- export type Eden<App extends Elysia<any>> = App['store'] extends {
48
- [key in typeof SCHEMA]: any
49
- }
50
- ? IsAny<Elysia> extends true
51
- ? 'Please install Elysia before using Eden'
52
- : UnionToIntersection<CreateEden<App['store'][typeof SCHEMA]>> & {
53
- $fn: CreateEdenFn<App['store'][typeof EXPOSED]>
54
- }
55
- : never
56
-
57
- export interface EdenCall {
58
- [x: string]: any
59
- $fetch?: RequestInit
60
- $query?: Record<string, string | boolean | number>
61
- }
62
-
63
- export type UnionToIntersection<U> = (
64
- U extends any ? (k: U) => void : never
65
- ) extends (k: infer I) => void
66
- ? I
67
- : never
68
-
69
- // https://twitter.com/mattpocockuk/status/1622730173446557697?s=20
70
- type Prettify<T> = {
71
- [K in keyof T]: T[K]
72
- } & {}
73
-
74
- type TypedRouteToParams<Route extends TypedRoute> =
75
- (Route['body'] extends NonNullable<Route['body']>
76
- ? Route['body'] extends Record<any, any>
77
- ? Route['body']
78
- : {
79
- $body: Route['body']
80
- }
81
- : {}) &
82
- (Route['query'] extends NonNullable<Route['query']>
83
- ? unknown extends Route['query']
84
- ? {}
85
- : {
86
- $query: Route['query']
87
- }
88
- : {})
89
-
90
- export type CreateEden<
91
- Server extends Record<string, Record<string, TypedRoute>>,
92
- // pathnames are always string
93
- Path extends string = keyof Server extends string ? keyof Server : never,
94
- Full extends string = ''
95
- > = Path extends `/${infer Start}`
96
- ? CreateEden<Server, Start, Path>
97
- : Path extends `${infer A}/${infer B}`
98
- ? // If path parameters, accept any string
99
- IsPathParameter<A> extends never
100
- ? {
101
- [key in A]: CreateEden<Server, B, Full>
102
- }
103
- : Record<string, CreateEden<Server, B, Full>> &
104
- Record<
105
- `$${A}`,
106
- `Expected path parameters ':${A}', replace this with any string`
107
- >
108
- : // Iterate until last string then catch method
109
- {
110
- [key in Path extends ''
111
- ? // If end with empty, then return as index
112
- 'index'
113
- : Path extends `:${infer params}`
114
- ? string
115
- : Path | CamelCase<Path>]: Full extends keyof Server
116
- ? {
117
- // Check if is method
118
- [key in keyof Server[Full] extends string
119
- ? Lowercase<keyof Server[Full]>
120
- : keyof Server[Full]]: [
121
- Server[Full][key extends string ? Uppercase<key> : key]
122
- ] extends [infer Route extends TypedRoute]
123
- ? undefined extends Route['body']
124
- ? (params?: {
125
- $query?: EdenCall['$query']
126
- $fetch?: EdenCall['$fetch']
127
- }) => Promise<
128
- Route['response'] extends {
129
- 200: infer ReturnedType
130
- }
131
- ?
132
- | ReturnedType
133
- | MapError<Route['response']>
134
- : unknown
135
- >
136
- : (
137
- params: Prettify<
138
- TypedRouteToParams<Route> & {
139
- $query?: EdenCall['$query']
140
- $fetch?: EdenCall['$fetch']
141
- }
142
- >
143
- ) => Promise<
144
- Route['response'] extends {
145
- 200: infer ReturnedType
146
- }
147
- ?
148
- | ReturnedType
149
- | MapError<Route['response']>
150
- : unknown
151
- >
152
- : key extends 'subscribe'
153
- ? // Since subscribe key is only a lower letter
154
- [
155
- Server[Full][key],
156
- Server[Full][key]['query']
157
- ] extends [
158
- infer Route extends TypedRoute,
159
- infer Query extends TypedRoute['query']
160
- ]
161
- ? unknown extends NonNullable<Query>
162
- ? (params?: {
163
- $query?: EdenCall['$query']
164
- }) => EdenWS<Route>
165
- : Query extends NonNullable<Query>
166
- ? (params: { $query: Query }) => EdenWS<Route>
167
- : (params?: {
168
- $query?: EdenCall['$query']
169
- }) => EdenWS<Route>
170
- : never
171
- : never
172
- }
173
- : never
174
- } & (Path extends `:${infer params}`
175
- ? Record<
176
- `$${params}`,
177
- `Expected path parameters ':${params}', replace this with any string`
178
- >
179
- : {})
180
-
181
- // https://stackoverflow.com/questions/59623524/typescript-how-to-map-type-keys-to-camelcase
182
- type CamelCase<S extends string> =
183
- S extends `${infer P1}-${infer P2}${infer P3}`
184
- ? `${Lowercase<P1>}${Uppercase<P2>}${CamelCase<P3>}`
185
- : Lowercase<S>
186
-
187
- export interface EdenWSOnMessage<Data = unknown> extends MessageEvent {
188
- data: Data
189
- rawData: MessageEvent['data']
190
- }
191
-
192
- export type EdenWSEvent<
193
- K extends keyof WebSocketEventMap,
194
- Data = unknown
195
- > = K extends 'message' ? EdenWSOnMessage<Data> : WebSocketEventMap[K]
196
-
197
- export interface EdenConfig {
198
- fn?: string
199
- fetch?: Omit<RequestInit, 'body'>
200
- }
11
+ // https://stackoverflow.com/a/39495173
12
+ type Range<F extends number, T extends number> = Exclude<
13
+ Enumerate<T>,
14
+ Enumerate<F>
15
+ >
201
16
 
202
17
  type Enumerate<
203
18
  N extends number,
@@ -206,15 +21,9 @@ type Enumerate<
206
21
  ? Acc[number]
207
22
  : Enumerate<N, [...Acc, Acc['length']]>
208
23
 
209
- // https://stackoverflow.com/a/39495173
210
- type Range<F extends number, T extends number> = Exclude<
211
- Enumerate<T>,
212
- Enumerate<F>
213
- >
214
-
215
24
  type ErrorRange = Range<300, 599>
216
25
 
217
- type MapError<T extends Record<number, unknown>> = [
26
+ export type MapError<T extends Record<number, unknown>> = [
218
27
  {
219
28
  [K in keyof T]-?: K extends ErrorRange ? K : never
220
29
  }[keyof T]
@@ -223,3 +32,34 @@ type MapError<T extends Record<number, unknown>> = [
223
32
  [K in A]: EdenFetchError<K, T[K]>
224
33
  }[A]
225
34
  : false
35
+
36
+ // https://twitter.com/mattpocockuk/status/1622730173446557697?s=20
37
+ type Prettify<T> = {
38
+ [K in keyof T]: T[K]
39
+ } & {}
40
+
41
+ export type UnionToIntersect<U> = (
42
+ U extends unknown ? (arg: U) => 0 : never
43
+ ) extends (arg: infer I) => 0
44
+ ? I
45
+ : never
46
+
47
+ export type IsAny<T> = 0 extends 1 & T ? true : false
48
+
49
+ export type IsNever<T> = [T] extends [never] ? true : false
50
+
51
+ export type IsUnknown<T> = IsAny<T> extends true
52
+ ? false
53
+ : unknown extends T
54
+ ? true
55
+ : false
56
+
57
+ export type AnyTypedRoute = {
58
+ body: unknown
59
+ headers: Record<string, any> | undefined
60
+ query: Record<string, any> | undefined
61
+ params: Record<string, any> | undefined
62
+ response: Record<string, unknown> & {
63
+ '200': unknown
64
+ }
65
+ }
package/src/utils.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import type { serialize, deserialize } from 'superjson'
2
- import type { EdenCall, EdenConfig } from './types'
3
2
 
4
3
  export class EdenFetchError<
5
4
  Status extends number = number,
@@ -15,107 +14,3 @@ export class EdenFetchError<
15
14
  this.value = value
16
15
  }
17
16
  }
18
-
19
-
20
- const camelToDash = (str: string) =>
21
- str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)
22
-
23
- export const composePath = (
24
- domain: string,
25
- path: string,
26
- query: EdenCall['$query'] | undefined
27
- ) => {
28
- if (!domain.endsWith('/')) domain += '/'
29
- path = camelToDash(path.replace(/index/g, ''))
30
-
31
- if (!query || !Object.keys(query).length) return `${domain}${path}`
32
-
33
- let q = ''
34
- for (const [key, value] of Object.entries(query)) q += `${key}=${value}&`
35
-
36
- return `${domain}${path}?${q.slice(0, -1)}`
37
- }
38
-
39
- export class Signal {
40
- private url: string
41
- private config: EdenConfig
42
-
43
- private pendings: Array<{ n: string[] } | { n: string[]; p: any }> = []
44
- private operation: Promise<any[]> | null = null
45
- private isFetching = false
46
-
47
- private sJson: Promise<{
48
- serialize: typeof serialize
49
- deserialize: typeof deserialize
50
- }>
51
-
52
- constructor(url: string, config: EdenConfig) {
53
- this.url = url
54
- this.config = config
55
-
56
- this.sJson = import('superjson').then((superJson) => {
57
- return {
58
- serialize: superJson.serialize,
59
- deserialize: superJson.deserialize
60
- }
61
- })
62
- }
63
-
64
- setConfig(config: EdenConfig) {
65
- this.config = config
66
- }
67
-
68
- clone(config?: EdenConfig) {
69
- return new Signal(this.url, config ?? this.config)
70
- }
71
-
72
- async run(procedure: string[], params: any) {
73
- const current = +this.pendings.length
74
- this.pendings.push(
75
- params !== undefined
76
- ? { n: procedure, p: params }
77
- : { n: procedure }
78
- )
79
-
80
- if (this.isFetching) return this.operation?.then((x) => x[current])
81
- this.isFetching = true
82
-
83
- this.operation = new Promise((resolve) => {
84
- setTimeout(async () => {
85
- const requests = [...this.pendings]
86
- this.pendings = []
87
-
88
- const { serialize, deserialize } = await this.sJson
89
-
90
- const results = await fetch(
91
- `${this.url}${this.config.fn ?? '/~fn'}`,
92
- {
93
- method: 'POST',
94
- ...this.config.fetch,
95
- headers: {
96
- 'content-type': 'elysia/fn',
97
- ...this.config.fetch?.headers
98
- },
99
- body: JSON.stringify(serialize(requests))
100
- }
101
- )
102
-
103
- if (results.status === 200)
104
- resolve(results.json().then((x) => deserialize(x as any)))
105
- else
106
- resolve(
107
- Array(requests.length).fill(
108
- new Error(await results.text())
109
- )
110
- )
111
- }, 33)
112
- })
113
-
114
- const result = await this.operation.then((results) => results[current])
115
-
116
- this.operation = null
117
- this.isFetching = false
118
-
119
- return result
120
- }
121
- }
package/dist/index.umd.js DELETED
@@ -1 +0,0 @@
1
- (function(h,d){typeof exports=="object"&&typeof module<"u"?d(exports):typeof define=="function"&&define.amd?define(["exports"],d):(h=typeof globalThis<"u"?globalThis:h||self,d(h["@elysia/eden"]={}))})(this,function(h){"use strict";class d extends Error{constructor(t,e){super(),this.status=t,this.value=e}}const N=i=>i.replace(/[A-Z]/g,t=>`-${t.toLowerCase()}`),P=(i,t,e)=>{if(i.endsWith("/")||(i+="/"),t=N(t.replace(/index/g,"")),!e||!Object.keys(e).length)return`${i}${t}`;let s="";for(const[o,n]of Object.entries(e))s+=`${o}=${n}&`;return`${i}${t}?${s.slice(0,-1)}`};class v{constructor(t,e){this.pendings=[],this.operation=null,this.isFetching=!1,this.url=t,this.config=e,this.sJson=import("superjson").then(s=>({serialize:s.serialize,deserialize:s.deserialize}))}setConfig(t){this.config=t}clone(t){return new v(this.url,t??this.config)}async run(t,e){var n;const s=+this.pendings.length;if(this.pendings.push(e!==void 0?{n:t,p:e}:{n:t}),this.isFetching)return(n=this.operation)==null?void 0:n.then(r=>r[s]);this.isFetching=!0,this.operation=new Promise(r=>{setTimeout(async()=>{var u;const a=[...this.pendings];this.pendings=[];const{serialize:p,deserialize:w}=await this.sJson,l=await fetch(`${this.url}${this.config.fn??"/~fn"}`,{method:"POST",...this.config.fetch,headers:{"content-type":"elysia/fn",...(u=this.config.fetch)==null?void 0:u.headers},body:JSON.stringify(p(a))});l.status===200?r(l.json().then(f=>w(f))):r(Array(a.length).fill(new Error(await l.text())))},33)});const o=await this.operation.then(r=>r[s]);return this.operation=null,this.isFetching=!1,o}}class j{constructor(t){this.ws=new WebSocket(t),this.url=t}send(t){return Array.isArray(t)?(t.forEach(e=>this.send(e)),this):(this.ws.send(typeof t=="object"?JSON.stringify(t):t.toString()),this)}on(t,e,s){return this.addEventListener(t,e,s)}off(t,e,s){return this.ws.removeEventListener(t,e,s),this}addEventListener(t,e,s){return this.ws.addEventListener(t,o=>{if(t==="message"){let n=o.data.toString();const r=n.charCodeAt(0);if(r===47||r===123)try{n=JSON.parse(n)}catch{}else Number.isNaN(+n)?n==="true"?n=!0:n==="fase"&&(n=!1):n=+n;e({...o,data:n})}else e(o)},s),this}removeEventListener(t,e,s){return this.off(t,e,s),this}close(){return this.ws.close(),this}}const b=(i,t,e)=>new Proxy((...s)=>{},{get(s,o,n){return b(i,[...t,o],e)},apply(s,o,n){const r=n[0];if(t.length===1){if(t[0]in Object.prototype||t[0]in Promise.prototype)return s(n);switch(t[0]){case"toJSON":return s(n);case"$set":return e.setConfig(r);case"$clone":return b(i,[],e.clone(r))}}return e.run(t,n).then(a=>{if(a instanceof Error)throw a;return a})}}),S=(i,t="",e)=>new Proxy(()=>{},{get(s,o,n){return S(i,`${t}/${o.toString()}`,e)},apply(s,o,[{$query:n,$fetch:r,$body:a,...p}={$fetch:void 0,$query:void 0,$body:void 0}]=[{}]){var E;const w=t.lastIndexOf("/"),l=t.slice(w+1),u=P(i,t.slice(0,w),n);if(l==="subscribe")return new j(u.replace(/^([^]+):\/\//,u.startsWith("https://")?"wss://":"ws://"));const f=a??(Object.keys(p).length?p:void 0),x=typeof f=="object";return fetch(u,{method:l,body:x?JSON.stringify(f):f,...e.fetch,...r,headers:f?{"content-type":x?"application/json":"text/plain",...(E=e.fetch)==null?void 0:E.headers,...r==null?void 0:r.headers}:void 0}).then(async c=>{var $,O;if(c.status>300){let y;if(($=c.headers.get("content-type"))!=null&&$.includes("application/json"))try{y=await c.json()}catch{y=await c.text()}else y=await c.text();return new d(c.status,y)}if((O=c.headers.get("content-type"))!=null&&O.includes("application/json"))try{return await c.json()}catch{}let g=await c.text();return Number.isNaN(+g)?g==="true"?!0:g==="false"?!1:g:+g})}}),m=(i,t={})=>new Proxy({},{get(e,s){return s==="$fn"?b(i,[],new v(i,t)):S(i,s,t)}});h.EdenWS=j,h.eden=m,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})});