@based/functions 1.0.1 → 1.1.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/channel.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { ChannelMessageFunction } from './functions'
2
+
3
+ export abstract class BasedChannel<K = any> {
4
+ abstract subscribe(
5
+ onMessage: ChannelMessageFunction<K>,
6
+ onError?: (err: any) => void
7
+ ): () => void
8
+
9
+ abstract publish(message: K): void
10
+ }
package/src/client.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  import { AuthState } from './auth'
2
+ import { BasedChannel } from './channel'
2
3
  import { Context } from './context'
3
4
  import { BasedQuery } from './query'
5
+ import { StreamFunctionOpts } from './stream'
4
6
 
7
+ // TODO: this is the place where we will add extra specifications
5
8
  export abstract class BasedFunctionClient {
6
9
  server: any
7
10
 
@@ -9,9 +12,18 @@ export abstract class BasedFunctionClient {
9
12
 
10
13
  abstract query(name: string, payload?: any): BasedQuery
11
14
 
12
- abstract stream(name: string, stream?: any, ctx?: Context): Promise<any>
15
+ abstract channel(name: string, payload?: any): BasedChannel
16
+
17
+ abstract stream(
18
+ name: string,
19
+ payload: StreamFunctionOpts,
20
+ ctx?: Context
21
+ ): Promise<any>
13
22
 
14
23
  abstract sendAuthState(ctx: Context, authState: AuthState): void
15
24
 
16
- abstract renewAuthState(ctx: Context, authState?: AuthState): void
25
+ abstract renewAuthState(
26
+ ctx: Context,
27
+ authState?: AuthState
28
+ ): Promise<AuthState>
17
29
  }
package/src/context.ts CHANGED
@@ -4,7 +4,7 @@ import { parseQuery } from '@saulx/utils'
4
4
  import { BasedFunctionClient } from './client'
5
5
 
6
6
  export type WebSocketSession = {
7
- // State can be used for anyting - for us the based class instance
7
+ // State can be used for anything - for us the based class instance
8
8
  state?: any
9
9
  query: string
10
10
  ua: string
@@ -13,18 +13,26 @@ export type WebSocketSession = {
13
13
  method: string
14
14
  authState: AuthState
15
15
  obs: Set<number>
16
- unauthorizedObs: Set<{
16
+ unauthorizedObs?: Set<{
17
17
  id: number
18
18
  checksum: number
19
19
  name: string
20
20
  payload: any
21
21
  }>
22
+ unauthorizedChannels?: Set<{
23
+ id: number
24
+ name: string
25
+ payload: any
26
+ }>
22
27
  // Optimization so we dont need to keep track of websockets outside of uws
23
28
  c?: Context<WebSocketSession>
24
- } & WebSocket
29
+ ws?: BasedWebSocket
30
+ }
31
+
32
+ export type BasedWebSocket = WebSocket<WebSocketSession>
25
33
 
26
34
  export type HttpSession = {
27
- // State can be used for anyting - for us the based class instance
35
+ // State can be used for anything - for us the based class instance
28
36
  state?: any
29
37
  res: HttpResponse
30
38
  req: HttpRequest
@@ -47,7 +55,13 @@ export type HttpSession = {
47
55
  export type InternalSessionObservable = {
48
56
  id: number
49
57
  name: string
50
- type: 'observable'
58
+ type: 'query'
59
+ }
60
+
61
+ export type InternalSessionChannel = {
62
+ id: number
63
+ name: string
64
+ type: 'channel'
51
65
  }
52
66
 
53
67
  export type InternalSessionClient = {
@@ -56,7 +70,10 @@ export type InternalSessionClient = {
56
70
  }
57
71
 
58
72
  // Internal session for internal functions
59
- export type InternalSession = InternalSessionClient | InternalSessionObservable
73
+ export type InternalSession =
74
+ | InternalSessionClient
75
+ | InternalSessionObservable
76
+ | InternalSessionChannel
60
77
 
61
78
  // used for minimal security errors (e.g. rate limit)
62
79
  export type MinimalExternalSession = {
@@ -64,11 +81,15 @@ export type MinimalExternalSession = {
64
81
  ip: string
65
82
  }
66
83
 
67
- export type Session =
84
+ export type Session = (
68
85
  | WebSocketSession
69
86
  | HttpSession
70
87
  | InternalSession
71
88
  | MinimalExternalSession
89
+ ) & {
90
+ /** Only available in Ws and Http contexts */
91
+ authState?: AuthState
92
+ }
72
93
 
73
94
  export type Context<S extends Session = Session> = {
74
95
  session?: S
@@ -92,8 +113,24 @@ export const isWsContext = (
92
113
  return false
93
114
  }
94
115
 
116
+ export const isClientContext = (
117
+ ctx: Context<Session>
118
+ ): ctx is Context<WebSocketSession | HttpSession> => {
119
+ if (ctx.session && (isWsSession(ctx.session) || isHttpSession(ctx.session))) {
120
+ return true
121
+ }
122
+ return false
123
+ }
124
+
125
+ export const isHttpSession = (session: Session): session is HttpSession => {
126
+ if ('res' in session) {
127
+ return true
128
+ }
129
+ return false
130
+ }
131
+
95
132
  export const isWsSession = (session: Session): session is WebSocketSession => {
96
- if (!('res' in session) && 'ip' in session && 'id' in session) {
133
+ if ('send' in session) {
97
134
  return true
98
135
  }
99
136
  return false
package/src/functions.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Context, HttpSession } from './context'
2
2
  import { BasedFunctionClient } from './client'
3
- import { Duplex } from 'stream'
3
+ import { BasedDataStream } from './stream'
4
4
 
5
5
  export type ObservableUpdateFunction<K = any> = {
6
6
  (
@@ -19,11 +19,23 @@ export type ObservableUpdateFunction<K = any> = {
19
19
  // TODO: use error package
20
20
  export type ObserveErrorListener = (err: any) => void
21
21
 
22
- export type CustomHttpResponse = (
23
- result: any,
24
- payload: any,
25
- client: Context<HttpSession>
26
- ) => Promise<boolean>
22
+ export type HttpHeaders = {
23
+ [header: string]: number | string | (string | number)[]
24
+ }
25
+
26
+ export type SendHttpResponse = (
27
+ responseData: any,
28
+ headers?: HttpHeaders,
29
+ status?: string | number
30
+ ) => void
31
+
32
+ export type HttpResponse<P = any, K = any> = (
33
+ based: BasedFunctionClient,
34
+ payload: P,
35
+ responseData: K,
36
+ send: SendHttpResponse,
37
+ ctx: Context<HttpSession>
38
+ ) => Promise<void>
27
39
 
28
40
  export type BasedFunction<P = any, K = any> = (
29
41
  based: BasedFunctionClient,
@@ -35,10 +47,7 @@ export type StreamPayload<P = any> = {
35
47
  payload?: P
36
48
  mimeType: string
37
49
  size: number
38
- stream: Duplex & {
39
- size: number
40
- receivedBytes: number
41
- }
50
+ stream: BasedDataStream
42
51
  fileName?: string
43
52
  extension?: string
44
53
  }
@@ -52,10 +61,37 @@ export type BasedQueryFunction<P = any, K = any> =
52
61
  | ((
53
62
  based: BasedFunctionClient,
54
63
  payload: P,
55
- update: ObservableUpdateFunction<K>
64
+ update: ObservableUpdateFunction<K>,
65
+ error: ObserveErrorListener
56
66
  ) => Promise<() => void>)
57
67
  | ((
58
68
  based: BasedFunctionClient,
59
69
  payload: P,
60
- update: ObservableUpdateFunction<K>
70
+ update: ObservableUpdateFunction<K>,
71
+ error: ObserveErrorListener
61
72
  ) => () => void)
73
+
74
+ export type BasedChannelFunction<P = any, K = any> = (
75
+ based: BasedFunctionClient,
76
+ payload: P,
77
+ id: number,
78
+ update: ChannelMessageFunction<K>,
79
+ error: (err?: any) => void
80
+ ) => () => void
81
+
82
+ export type BasedChannelPublishFunction<P = any, K = any> = (
83
+ based: BasedFunctionClient,
84
+ payload: P,
85
+ message: K,
86
+ id: number,
87
+ ctx: Context
88
+ ) => void
89
+
90
+ export type ChannelMessageFunction<K = any> = (message: K) => void
91
+
92
+ export type ChannelMessageFunctionInternal<K = any> = (
93
+ message: K,
94
+ err?: any
95
+ ) => void
96
+
97
+ export type UninstallFunction = () => Promise<void>
package/src/index.ts CHANGED
@@ -3,3 +3,5 @@ export * from './auth'
3
3
  export * from './client'
4
4
  export * from './functions'
5
5
  export * from './query'
6
+ export * from './stream'
7
+ export * from './channel'
package/src/query.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  import { ObservableUpdateFunction, ObserveErrorListener } from './functions'
2
2
 
3
- export abstract class BasedQuery {
3
+ export abstract class BasedQuery<K = any> {
4
4
  abstract subscribe(
5
- onData: ObservableUpdateFunction,
5
+ onData: ObservableUpdateFunction<K>,
6
6
  onError?: ObserveErrorListener
7
7
  ): () => void
8
8
 
9
9
  abstract getWhen(
10
- condition: (data: any, checksum: number) => boolean
10
+ condition: (data: K, checksum: number) => boolean
11
11
  ): Promise<any>
12
12
 
13
- abstract get(): Promise<any>
13
+ abstract get(): Promise<K>
14
14
  }
package/src/stream.ts ADDED
@@ -0,0 +1,95 @@
1
+ import { Duplex, Readable } from 'stream'
2
+ import util from 'node:util'
3
+
4
+ // prob want to move this to based functions
5
+ export class BasedDataStream extends Duplex {
6
+ public size: number = 0
7
+ public receivedBytes: number = 0
8
+ public progessTimer: NodeJS.Timeout
9
+
10
+ constructor(size: number) {
11
+ super()
12
+ this.size = size
13
+ this.emit('progress', 0)
14
+ }
15
+
16
+ _read() {}
17
+
18
+ _write(chunk, encoding, callback) {
19
+ this.receivedBytes += chunk.byteLength
20
+ if (this.size && this.size > 20000) {
21
+ if (!this.progessTimer) {
22
+ this.progessTimer = setTimeout(() => {
23
+ const progress = this.receivedBytes / this.size
24
+ this.emit('progress', progress)
25
+ this.progessTimer = null
26
+ }, 200)
27
+ }
28
+ }
29
+ this.push(Buffer.from(chunk, encoding))
30
+ callback()
31
+ }
32
+
33
+ _final() {
34
+ if (!this.size) {
35
+ this.size = this.receivedBytes
36
+ }
37
+ this.receivedBytes = this.size
38
+ if (this.progessTimer) {
39
+ clearTimeout(this.progessTimer)
40
+ this.progessTimer = null
41
+ }
42
+ this.emit('progress', 1)
43
+ this.push(null)
44
+ }
45
+
46
+ [util.inspect.custom]() {
47
+ if (this.size) {
48
+ const rb =
49
+ this.receivedBytes < 1000
50
+ ? Math.round(this.receivedBytes / 1024) + 'kb'
51
+ : Math.round(this.receivedBytes / 1024 / 1024) + 'mb'
52
+
53
+ return `[BasedStream ${~~(
54
+ (this.receivedBytes / this.size) *
55
+ 100
56
+ )}% ${rb}]`
57
+ } else {
58
+ return `[BasedStream]`
59
+ }
60
+ }
61
+ }
62
+
63
+ // maybe make a type pkg
64
+ export type StreamFunctionContents<F = Buffer | ArrayBuffer | string> = {
65
+ contents: F
66
+ payload?: any
67
+ mimeType?: string
68
+ fileName?: string
69
+ }
70
+
71
+ export type StreamFunctionStream =
72
+ | {
73
+ contents: Readable | Duplex
74
+ payload?: any
75
+ size: number
76
+ mimeType?: string
77
+ fileName?: string
78
+ extension?: string
79
+ }
80
+ | {
81
+ contents: BasedDataStream
82
+ payload?: any
83
+ size?: number
84
+ mimeType?: string
85
+ fileName?: string
86
+ extension?: string
87
+ }
88
+
89
+ export type StreamFunctionOpts = StreamFunctionContents | StreamFunctionStream
90
+
91
+ export const isStreamFunctionOpts = (
92
+ opts: StreamFunctionContents | StreamFunctionStream
93
+ ): opts is StreamFunctionStream => {
94
+ return opts.contents instanceof Duplex || opts.contents instanceof Readable
95
+ }