@pikku/core 0.7.0 → 0.7.2

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.
Files changed (95) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/channel/channel-runner.js +1 -1
  3. package/dist/channel/serverless/serverless-channel-runner.js +1 -1
  4. package/dist/function/function-runner.js +7 -5
  5. package/dist/http/http-runner.js +4 -2
  6. package/dist/http/log-http-routes.js +1 -1
  7. package/lcov.info +4596 -0
  8. package/package.json +1 -1
  9. package/src/channel/channel-handler.ts +165 -0
  10. package/src/channel/channel-runner.ts +153 -0
  11. package/src/channel/channel-store.ts +29 -0
  12. package/src/channel/channel.types.ts +133 -0
  13. package/src/channel/eventhub-service.ts +30 -0
  14. package/src/channel/eventhub-store.ts +8 -0
  15. package/src/channel/index.ts +8 -0
  16. package/src/channel/local/index.ts +3 -0
  17. package/src/channel/local/local-channel-handler.ts +50 -0
  18. package/src/channel/local/local-channel-runner.test.ts +157 -0
  19. package/src/channel/local/local-channel-runner.ts +139 -0
  20. package/src/channel/local/local-eventhub-service.test.ts +95 -0
  21. package/src/channel/local/local-eventhub-service.ts +95 -0
  22. package/src/channel/log-channels.ts +20 -0
  23. package/src/channel/pikku-abstract-channel-handler.test.ts +52 -0
  24. package/src/channel/pikku-abstract-channel-handler.ts +38 -0
  25. package/src/channel/serverless/index.ts +1 -0
  26. package/src/channel/serverless/serverless-channel-runner.ts +220 -0
  27. package/src/errors/error-handler.ts +61 -0
  28. package/src/errors/error.test.ts +54 -0
  29. package/src/errors/errors.ts +339 -0
  30. package/src/errors/index.ts +2 -0
  31. package/src/function/function-runner.ts +92 -0
  32. package/src/function/functions.types.ts +77 -0
  33. package/src/function/index.ts +2 -0
  34. package/src/handle-error.ts +73 -0
  35. package/src/http/http-runner.test.ts +140 -0
  36. package/src/http/http-runner.ts +493 -0
  37. package/src/http/http.types.ts +250 -0
  38. package/src/http/incomingmessage-to-request-convertor.ts +0 -0
  39. package/src/http/index.ts +8 -0
  40. package/src/http/log-http-routes.ts +22 -0
  41. package/src/http/pikku-fetch-http-request.test.ts +237 -0
  42. package/src/http/pikku-fetch-http-request.ts +163 -0
  43. package/src/http/pikku-fetch-http-response.test.ts +82 -0
  44. package/src/http/pikku-fetch-http-response.ts +138 -0
  45. package/src/index.ts +20 -0
  46. package/src/middleware/auth-apikey.ts +66 -0
  47. package/src/middleware/auth-bearer.ts +66 -0
  48. package/src/middleware/auth-cookie.ts +103 -0
  49. package/src/middleware/index.ts +3 -0
  50. package/src/middleware/timeout.ts +13 -0
  51. package/src/middleware-runner.ts +43 -0
  52. package/src/permissions.test.ts +58 -0
  53. package/src/permissions.ts +43 -0
  54. package/src/pikku-function.ts +1 -0
  55. package/src/pikku-request.ts +23 -0
  56. package/src/pikku-response.ts +5 -0
  57. package/src/pikku-state.ts +87 -0
  58. package/src/scheduler/index.ts +5 -0
  59. package/src/scheduler/log-schedulers.ts +20 -0
  60. package/src/scheduler/scheduler-runner.ts +105 -0
  61. package/src/scheduler/scheduler.types.ts +33 -0
  62. package/src/schema.test.ts +57 -0
  63. package/src/schema.ts +99 -0
  64. package/src/services/content-service.ts +68 -0
  65. package/src/services/index.ts +18 -0
  66. package/src/services/jwt-service.ts +30 -0
  67. package/src/services/local-content.ts +105 -0
  68. package/src/services/local-secrets.ts +43 -0
  69. package/src/services/local-variables.ts +17 -0
  70. package/src/services/logger-console.ts +97 -0
  71. package/src/services/logger.ts +57 -0
  72. package/src/services/schema-service.ts +26 -0
  73. package/src/services/secret-service.ts +17 -0
  74. package/src/services/user-session-service.ts +53 -0
  75. package/src/services/variables-service.ts +6 -0
  76. package/src/time-utils.test.ts +56 -0
  77. package/src/time-utils.ts +32 -0
  78. package/src/types/core.types.ts +178 -0
  79. package/src/utils.ts +40 -0
  80. package/tsconfig.json +13 -0
  81. package/tsconfig.tsbuildinfo +1 -0
  82. package/dist/http/http-route-runner.d.ts +0 -90
  83. package/dist/http/http-route-runner.js +0 -325
  84. package/dist/http/http-routes.types.d.ts +0 -179
  85. package/dist/http/http-routes.types.js +0 -1
  86. package/dist/http/pikku-http-request.d.ts +0 -56
  87. package/dist/http/pikku-http-request.js +0 -95
  88. package/dist/http/pikku-http-response.d.ts +0 -13
  89. package/dist/http/pikku-http-response.js +0 -60
  90. package/dist/parse-relative-time-offset.d.ts +0 -12
  91. package/dist/parse-relative-time-offset.js +0 -20
  92. package/dist/pikku-func.d.ts +0 -12
  93. package/dist/pikku-func.js +0 -39
  94. package/dist/types/functions.types.d.ts +0 -37
  95. package/dist/types/functions.types.js +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/core",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "author": "yasser.fadl@gmail.com",
5
5
  "license": "MIT",
6
6
  "module": "dist/index.js",
@@ -0,0 +1,165 @@
1
+ import {
2
+ CoreServices,
3
+ JSONValue,
4
+ CoreUserSession,
5
+ } from '../types/core.types.js'
6
+ import {
7
+ ChannelMessageMeta,
8
+ CoreAPIChannel,
9
+ PikkuChannelHandler,
10
+ } from './channel.types.js'
11
+ import { pikkuState } from '../pikku-state.js'
12
+ import { runPikkuFunc } from '../function/function-runner.js'
13
+
14
+ const getRouteMeta = (
15
+ channelName: string,
16
+ routingProperty?: string,
17
+ routerValue?: string
18
+ ): ChannelMessageMeta => {
19
+ const channelMeta = pikkuState('channel', 'meta')[channelName]
20
+ if (!channelMeta) {
21
+ throw new Error(`Channel ${channelName} not found`)
22
+ }
23
+ if (!routingProperty) {
24
+ if (!channelMeta.message) {
25
+ throw new Error(`Channel ${channelName} has no default message route`)
26
+ }
27
+ return channelMeta.message
28
+ }
29
+ if (!routerValue) {
30
+ throw new Error(
31
+ `Channel ${channelName} requires a router value for ${routingProperty}`
32
+ )
33
+ }
34
+ const route = channelMeta.messageRoutes[routingProperty]?.[routerValue]
35
+ if (!route) {
36
+ throw new Error(
37
+ `Channel ${channelName} has no route for ${routingProperty}:${routerValue}`
38
+ )
39
+ }
40
+ return route
41
+ }
42
+
43
+ const validateAuth = (
44
+ requiresSession: boolean,
45
+ session: CoreUserSession | undefined,
46
+ onMessage: any
47
+ ) => {
48
+ const auth =
49
+ typeof onMessage === 'function'
50
+ ? requiresSession
51
+ : onMessage.auth === undefined
52
+ ? requiresSession
53
+ : onMessage.auth
54
+
55
+ if (auth && !session) {
56
+ return false
57
+ }
58
+ return true
59
+ }
60
+
61
+ export const processMessageHandlers = (
62
+ services: CoreServices,
63
+ session: CoreUserSession | undefined,
64
+ channelConfig: CoreAPIChannel<any, any>,
65
+ channelHandler: PikkuChannelHandler
66
+ ) => {
67
+ const logger = services.logger
68
+ const requiresSession = channelConfig.auth !== false
69
+
70
+ const processMessage = async (
71
+ data: JSONValue,
72
+ onMessage: any,
73
+ session: CoreUserSession | undefined,
74
+ routingProperty?: string,
75
+ routerValue?: string
76
+ ): Promise<unknown> => {
77
+ if (!validateAuth(requiresSession, channelHandler, onMessage)) {
78
+ const routeMessage = routingProperty
79
+ ? `route '${routingProperty}:${routerValue}'`
80
+ : 'the default message route'
81
+ logger.error(
82
+ `Channel ${channelConfig.name} with id ${channelHandler.getChannel().channelId} requires a session for ${routeMessage}`
83
+ )
84
+ // TODO: Send error message back breaks typescript, but should be implemented somehow
85
+ channelHandler.getChannel().send(`Unauthorized for ${routeMessage}`)
86
+ return
87
+ }
88
+
89
+ const { pikkuFuncName } = getRouteMeta(
90
+ channelConfig.name,
91
+ routingProperty,
92
+ routerValue
93
+ )
94
+
95
+ const permissions =
96
+ typeof onMessage === 'function' ? {} : onMessage.permissions
97
+
98
+ return await runPikkuFunc(pikkuFuncName, {
99
+ singletonServices: services,
100
+ getAllServices: () => ({
101
+ ...services,
102
+ channel: channelHandler.getChannel(),
103
+ }),
104
+ data,
105
+ session,
106
+ permissions,
107
+ })
108
+ }
109
+
110
+ const onMessage = async (rawData): Promise<unknown> => {
111
+ let result: unknown
112
+ let processed = false
113
+
114
+ // Route-specific handling
115
+ if (typeof rawData === 'string' && channelConfig.onMessageRoute) {
116
+ try {
117
+ const messageData = JSON.parse(rawData)
118
+ const entries = Object.entries(channelConfig.onMessageRoute)
119
+ for (const [routingProperty, routes] of entries) {
120
+ const routerValue = messageData[routingProperty]
121
+ if (routerValue && routes[routerValue]) {
122
+ processed = true
123
+ result = await processMessage(
124
+ messageData,
125
+ routes[routerValue],
126
+ session,
127
+ routingProperty,
128
+ routerValue
129
+ )
130
+ break
131
+ }
132
+ }
133
+
134
+ // Default handler if no routes matched but json data was parsed
135
+ if (!processed && channelConfig.onMessage) {
136
+ processed = true
137
+ result = await processMessage(
138
+ messageData,
139
+ channelConfig.onMessage,
140
+ session
141
+ )
142
+ }
143
+ } catch (error) {
144
+ // Most likely a json error.. ignore
145
+ }
146
+ }
147
+
148
+ // Default handler if no routes matched and json data wasn't parsed
149
+ if (!processed && channelConfig.onMessage) {
150
+ processed = true
151
+ result = await processMessage(rawData, channelConfig.onMessage, session)
152
+ }
153
+
154
+ if (!processed) {
155
+ logger.error(
156
+ `No handler found for message in channel ${channelConfig.name} for ${JSON.stringify(rawData)}`
157
+ )
158
+ logger.error(`Channel ${JSON.stringify(channelConfig)}`)
159
+ }
160
+
161
+ return result
162
+ }
163
+
164
+ return onMessage
165
+ }
@@ -0,0 +1,153 @@
1
+ import { NotFoundError } from '../errors/errors.js'
2
+ import { addFunction } from '../function/function-runner.js'
3
+ import { pikkuState } from '../pikku-state.js'
4
+ import { coerceTopLevelDataFromSchema, validateSchema } from '../schema.js'
5
+ import { UserSessionService } from '../services/user-session-service.js'
6
+ import {
7
+ ChannelMeta,
8
+ CoreAPIChannel,
9
+ RunChannelOptions,
10
+ RunChannelParams,
11
+ } from './channel.types.js'
12
+ import { match } from 'path-to-regexp'
13
+
14
+ /**
15
+ * Adds a channel and registers all functions referenced in it using the
16
+ * function names already stored in the channel metadata
17
+ */
18
+ export const addChannel = <
19
+ In,
20
+ Channel extends string,
21
+ ChannelFunction,
22
+ ChannelFunctionSessionless,
23
+ APIPermission,
24
+ >(
25
+ channel: CoreAPIChannel<In, Channel, ChannelFunction, APIPermission>
26
+ ) => {
27
+ // Get the channel metadata
28
+ const channelsMeta = pikkuState('channel', 'meta')
29
+ const channelMeta = channelsMeta[channel.name]
30
+ if (!channelMeta) {
31
+ throw new Error(`Channel metadata not found for channel: ${channel.name}`)
32
+ }
33
+
34
+ pikkuState('channel', 'channels').set(channel.name, channel as any)
35
+
36
+ // Register onConnect function if provided
37
+ if (channel.onConnect && channelMeta.connectPikkuFuncName) {
38
+ addFunction(channelMeta.connectPikkuFuncName, channel.onConnect as any)
39
+ }
40
+
41
+ // Register onDisconnect function if provided
42
+ if (channel.onDisconnect && channelMeta.disconnectPikkuFuncName) {
43
+ addFunction(
44
+ channelMeta.disconnectPikkuFuncName,
45
+ channel.onDisconnect as any
46
+ )
47
+ }
48
+
49
+ // Register onMessage function if provided
50
+ if (channel.onMessage && channelMeta.message?.pikkuFuncName) {
51
+ const messageFunc =
52
+ typeof channel.onMessage === 'function'
53
+ ? channel.onMessage
54
+ : channel.onMessage.func
55
+ addFunction(channelMeta.message.pikkuFuncName, messageFunc as any)
56
+ }
57
+
58
+ // Register functions in onMessageRoute
59
+ if (channel.onMessageRoute && channelMeta.messageRoutes) {
60
+ // Iterate through each channel in onMessageRoute
61
+ Object.entries(channel.onMessageRoute).forEach(([channelKey, routes]) => {
62
+ const channelRoutes = channelMeta.messageRoutes[channelKey]
63
+ if (!channelRoutes) return
64
+
65
+ // Iterate through each route in the channel
66
+ Object.entries(routes).forEach(([routeKey, handler]) => {
67
+ const routeMeta = channelRoutes[routeKey]
68
+ if (!routeMeta) return
69
+
70
+ // Extract the function from the handler
71
+ const routeFunc = typeof handler === 'function' ? handler : handler.func
72
+
73
+ // Register the function using the pikku name from metadata
74
+ addFunction(routeMeta.pikkuFuncName, routeFunc as any)
75
+ })
76
+ })
77
+ }
78
+
79
+ // Store the channel configuration
80
+ pikkuState('channel', 'channels').set(channel.name, channel as any)
81
+ }
82
+
83
+ const getMatchingChannelConfig = (request: string) => {
84
+ const channels = pikkuState('channel', 'channels')
85
+ const channelsMeta = pikkuState('channel', 'meta')
86
+ for (const channelConfig of channels.values()) {
87
+ const cleanedRoute = channelConfig.route.replace(/^\/\//, '/')
88
+ const cleanedRequest = request.replace(/^\/\//, '/')
89
+ const matchFunc = match(cleanedRoute, {
90
+ decode: decodeURIComponent,
91
+ })
92
+ const matchedPath = matchFunc(cleanedRequest)
93
+ if (matchedPath) {
94
+ const channelMeta = channelsMeta[channelConfig.name]
95
+ if (!channelMeta) {
96
+ throw new Error(`Channel ${channelConfig.name} not found in metadata`)
97
+ }
98
+ return {
99
+ matchedPath,
100
+ params: matchedPath.params,
101
+ channelConfig,
102
+ schemaName: channelMeta.input,
103
+ meta: channelMeta,
104
+ }
105
+ }
106
+ }
107
+ return null
108
+ }
109
+
110
+ export const openChannel = async ({
111
+ route,
112
+ singletonServices,
113
+ coerceDataFromSchema = true,
114
+ request,
115
+ }: Pick<CoreAPIChannel<unknown, string>, 'route'> &
116
+ RunChannelParams<unknown> & {
117
+ userSession: UserSessionService<any>
118
+ } & RunChannelOptions): Promise<{
119
+ openingData: unknown
120
+ channelConfig: CoreAPIChannel<unknown, any>
121
+ meta: ChannelMeta
122
+ }> => {
123
+ const matchingChannel = getMatchingChannelConfig(route)
124
+ if (!matchingChannel) {
125
+ singletonServices.logger.info(`Channel not found: ${route}`)
126
+ throw new NotFoundError(`Channel not found: ${route}`)
127
+ }
128
+
129
+ const { params, channelConfig, schemaName, meta } = matchingChannel
130
+
131
+ const requiresSession = channelConfig.auth !== false
132
+ request?.setParams(params)
133
+
134
+ singletonServices.logger.info(
135
+ `Matched channel: ${channelConfig.name} | route: ${channelConfig.route} | auth: ${requiresSession.toString()}`
136
+ )
137
+
138
+ let openingData: any | undefined
139
+ if (request) {
140
+ openingData = await request.data()
141
+ if (coerceDataFromSchema && schemaName) {
142
+ coerceTopLevelDataFromSchema(schemaName, openingData)
143
+ }
144
+ await validateSchema(
145
+ singletonServices.logger,
146
+ singletonServices.schema,
147
+ schemaName,
148
+ openingData
149
+ )
150
+ }
151
+
152
+ return { openingData, channelConfig, meta }
153
+ }
@@ -0,0 +1,29 @@
1
+ import { CoreUserSession } from '../types/core.types.js'
2
+
3
+ export type Channel<ChannelType = unknown, OpeningData = unknown> = {
4
+ channelId: string
5
+ channelName: string
6
+ channelObject?: ChannelType
7
+ openingData?: OpeningData
8
+ }
9
+
10
+ export abstract class ChannelStore<
11
+ ChannelType = unknown,
12
+ OpeningData = unknown,
13
+ UserSession extends CoreUserSession = CoreUserSession,
14
+ TypedChannel = Channel<ChannelType, OpeningData>,
15
+ > {
16
+ public abstract addChannel(
17
+ channel: Channel<ChannelType, OpeningData>
18
+ ): Promise<void> | void
19
+ public abstract removeChannels(channelId: string[]): Promise<void> | void
20
+ public abstract setUserSession(
21
+ channelId: string,
22
+ userSession: UserSession | null
23
+ ): Promise<void> | void
24
+ public abstract getChannelAndSession(
25
+ channelId: string
26
+ ):
27
+ | Promise<TypedChannel & { session: UserSession }>
28
+ | (TypedChannel & { session: UserSession })
29
+ }
@@ -0,0 +1,133 @@
1
+ import { PikkuError } from '../errors/error-handler.js'
2
+ import {
3
+ HTTPFunctionMetaInputTypes,
4
+ PikkuHTTPRequest,
5
+ PikkuHTTPResponse,
6
+ } from '../http/http.types.js'
7
+ import {
8
+ APIDocs,
9
+ CoreSingletonServices,
10
+ CreateSessionServices,
11
+ PikkuMiddleware,
12
+ } from '../types/core.types.js'
13
+ import {
14
+ CoreAPIFunction,
15
+ CoreAPIFunctionSessionless,
16
+ CoreAPIPermission,
17
+ } from '../function/functions.types.js'
18
+
19
+ export type RunChannelOptions = Partial<{
20
+ skipUserSession: boolean
21
+ respondWith404: boolean
22
+ coerceDataFromSchema: boolean
23
+ logWarningsForStatusCodes: number[]
24
+ bubbleErrors: boolean
25
+ }>
26
+
27
+ export type RunChannelParams<ChannelData> = {
28
+ channelId: string
29
+ singletonServices: CoreSingletonServices
30
+ request?: PikkuHTTPRequest<ChannelData>
31
+ response?: PikkuHTTPResponse
32
+ createSessionServices?: CreateSessionServices
33
+ }
34
+
35
+ export interface ChannelMessageMeta {
36
+ pikkuFuncName: string
37
+ inputs: string[] | null
38
+ outputs: string[] | null
39
+ docs?: APIDocs
40
+ }
41
+
42
+ export interface ChannelMeta {
43
+ name: string
44
+ route: string
45
+ params?: string[]
46
+ query?: string[]
47
+ input: string | null
48
+ inputTypes?: HTTPFunctionMetaInputTypes
49
+ connect: boolean
50
+ connectPikkuFuncName: string | null
51
+ disconnect: boolean
52
+ disconnectPikkuFuncName: string | null
53
+ message: ChannelMessageMeta | null
54
+ messageRoutes: Record<string, Record<string, ChannelMessageMeta>>
55
+ docs?: APIDocs
56
+ tags?: string[]
57
+ }
58
+
59
+ export type ChannelsMeta = Record<string, ChannelMeta>
60
+
61
+ export type CoreAPIChannel<
62
+ ChannelData,
63
+ Channel extends string,
64
+ ChannelConnect =
65
+ | CoreAPIFunction<void, unknown, ChannelData>
66
+ | CoreAPIFunctionSessionless<void, unknown, ChannelData>,
67
+ ChannelDisconnect =
68
+ | CoreAPIFunction<void, void, ChannelData>
69
+ | CoreAPIFunctionSessionless<void, void, ChannelData>,
70
+ ChannelFunctionMessage =
71
+ | CoreAPIFunction<unknown, unknown, ChannelData>
72
+ | CoreAPIFunctionSessionless<unknown, unknown, ChannelData>,
73
+ APIPermission = CoreAPIPermission<ChannelData>,
74
+ > = {
75
+ name: string
76
+ route: Channel
77
+ onConnect?: ChannelConnect
78
+ onDisconnect?: ChannelDisconnect
79
+ onMessage?:
80
+ | {
81
+ func: ChannelFunctionMessage
82
+ permissions?: Record<string, APIPermission[] | APIPermission>
83
+ auth?: boolean
84
+ }
85
+ | ChannelFunctionMessage
86
+ onMessageRoute?: Record<
87
+ string,
88
+ Record<
89
+ string,
90
+ | ChannelFunctionMessage
91
+ | {
92
+ func: ChannelFunctionMessage
93
+ permissions?: Record<string, APIPermission[] | APIPermission>
94
+ auth?: boolean
95
+ }
96
+ >
97
+ >
98
+ middleware?: PikkuMiddleware[]
99
+ permissions?: Record<string, APIPermission[] | APIPermission>
100
+ auth?: boolean
101
+ docs?: Partial<{
102
+ description: string
103
+ response: string
104
+ errors: Array<typeof PikkuError>
105
+ tags: string[]
106
+ }>
107
+ tags?: string[]
108
+ }
109
+
110
+ export interface PikkuChannel<OpeningData, Out> {
111
+ // The channel identifier
112
+ channelId: string
113
+ // The data the channel was created with. This could be query parameters
114
+ // or parameters in the url.
115
+ openingData: OpeningData
116
+ // The data to send. This will fail is the stream has been closed.
117
+ send: (data: Out, isBinary?: boolean) => Promise<void> | void
118
+ // This will close the channel.
119
+ close: () => Promise<void> | void
120
+ // The current state of the channel
121
+ state: 'initial' | 'open' | 'closed'
122
+ }
123
+
124
+ export interface PikkuChannelHandler<OpeningData = unknown, Out = unknown> {
125
+ send(message: Out, isBinary?: boolean): Promise<void> | void
126
+ getChannel(): PikkuChannel<OpeningData, Out>
127
+ }
128
+
129
+ export type PikkuChannelHandlerFactory<OpeningData = unknown, Out = unknown> = (
130
+ channelId: string,
131
+ channelName: string,
132
+ openingData: OpeningData
133
+ ) => PikkuChannelHandler<OpeningData, Out>
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Interface defining the methods of a EventHub Service.
3
+ */
4
+ export interface EventHubService<Out = unknown> {
5
+ /**
6
+ * Subscribes a connection to a specific topic.
7
+ * @param topic - The topic to subscribe to.
8
+ * @param channelId - The unique ID of the connection to subscribe.
9
+ */
10
+ subscribe(topic: string, channelId: string): Promise<void> | void
11
+
12
+ /**
13
+ * Unsubscribes a connection from a specific topic.
14
+ * @param topic - The topic to unsubscribe from.
15
+ * @param channelId - The unique ID of the connection to unsubscribe.
16
+ */
17
+ unsubscribe(topic: string, channelId: string): Promise<void> | void
18
+
19
+ /**
20
+ * Sends data to all connections subscribed to a topic.
21
+ * @param topic - The topic to send data to.
22
+ * @param data - The data to send to the subscribers.
23
+ */
24
+ publish(
25
+ topic: string,
26
+ channelId: string | null,
27
+ data: Out,
28
+ isBinary?: boolean
29
+ ): Promise<void> | void
30
+ }
@@ -0,0 +1,8 @@
1
+ export abstract class EventHubStore {
2
+ public abstract getChannelIdsForTopic(topic: string): Promise<string[]>
3
+ public abstract subscribe(topic: string, channelId: string): Promise<boolean>
4
+ public abstract unsubscribe(
5
+ topic: string,
6
+ channelId: string
7
+ ): Promise<boolean>
8
+ }
@@ -0,0 +1,8 @@
1
+ export * from './channel-runner.js'
2
+ export * from './channel.types.js'
3
+ export * from './log-channels.js'
4
+ export * from './pikku-abstract-channel-handler.js'
5
+ export * from './eventhub-service.js'
6
+ export * from './eventhub-service.js'
7
+ export * from './channel-store.js'
8
+ export * from './eventhub-store.js'
@@ -0,0 +1,3 @@
1
+ export * from './local-channel-handler.js'
2
+ export * from './local-channel-runner.js'
3
+ export * from './local-eventhub-service.js'
@@ -0,0 +1,50 @@
1
+ import { PikkuAbstractChannelHandler } from '../pikku-abstract-channel-handler.js'
2
+
3
+ export class PikkuLocalChannelHandler<
4
+ OpeningData = unknown,
5
+ Out = unknown,
6
+ > extends PikkuAbstractChannelHandler<OpeningData, Out> {
7
+ private onMessageCallback?: (message: unknown) => void
8
+ private openCallBack?: () => void
9
+ private closeCallback?: () => void
10
+ private sendCallback?: (message: Out, isBinary?: boolean) => void
11
+
12
+ public registerOnOpen(callback: () => void): void {
13
+ this.openCallBack = callback
14
+ }
15
+
16
+ public open() {
17
+ this.getChannel().state = 'open'
18
+ if (this.openCallBack) {
19
+ this.openCallBack()
20
+ }
21
+ }
22
+
23
+ public registerOnMessage(callback: (data: any) => Promise<unknown>): void {
24
+ this.onMessageCallback = callback
25
+ }
26
+
27
+ public async message(data: unknown): Promise<unknown> {
28
+ return this.onMessageCallback?.(data)
29
+ }
30
+
31
+ public registerOnClose(callback: () => void): void {
32
+ this.closeCallback = callback
33
+ }
34
+
35
+ public close() {
36
+ super.close()
37
+ this.closeCallback?.()
38
+ }
39
+
40
+ public registerOnSend(send: (message: Out) => void) {
41
+ this.sendCallback = send
42
+ }
43
+
44
+ public send(message: Out, isBinary?: boolean): void {
45
+ if (!this.sendCallback) {
46
+ throw new Error('No send callback registered')
47
+ }
48
+ return this.sendCallback?.(message, isBinary)
49
+ }
50
+ }