@based/functions 1.0.2 → 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): 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
@@ -50,13 +58,22 @@ export type InternalSessionObservable = {
50
58
  type: 'query'
51
59
  }
52
60
 
61
+ export type InternalSessionChannel = {
62
+ id: number
63
+ name: string
64
+ type: 'channel'
65
+ }
66
+
53
67
  export type InternalSessionClient = {
54
68
  client: BasedFunctionClient
55
69
  type: 'client'
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
@@ -94,15 +115,22 @@ export const isWsContext = (
94
115
 
95
116
  export const isClientContext = (
96
117
  ctx: Context<Session>
97
- ): ctx is Context<WebSocketSession> => {
98
- if (ctx.session && 'authState' in ctx.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) {
99
127
  return true
100
128
  }
101
129
  return false
102
130
  }
103
131
 
104
132
  export const isWsSession = (session: Session): session is WebSocketSession => {
105
- if (!('res' in session) && 'ip' in session && 'id' in session) {
133
+ if ('send' in session) {
106
134
  return true
107
135
  }
108
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
  (
@@ -47,10 +47,7 @@ export type StreamPayload<P = any> = {
47
47
  payload?: P
48
48
  mimeType: string
49
49
  size: number
50
- stream: Duplex & {
51
- size: number
52
- receivedBytes: number
53
- }
50
+ stream: BasedDataStream
54
51
  fileName?: string
55
52
  extension?: string
56
53
  }
@@ -65,11 +62,36 @@ export type BasedQueryFunction<P = any, K = any> =
65
62
  based: BasedFunctionClient,
66
63
  payload: P,
67
64
  update: ObservableUpdateFunction<K>,
68
- error?: ObserveErrorListener
65
+ error: ObserveErrorListener
69
66
  ) => Promise<() => void>)
70
67
  | ((
71
68
  based: BasedFunctionClient,
72
69
  payload: P,
73
70
  update: ObservableUpdateFunction<K>,
74
- error?: ObserveErrorListener
71
+ error: ObserveErrorListener
75
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
+ }