@pikku/core 0.6.27 → 0.7.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/CHANGELOG.md +16 -1
- package/dist/channel/channel-handler.js +16 -38
- package/dist/channel/channel-runner.d.ts +7 -8
- package/dist/channel/channel-runner.js +58 -7
- package/dist/channel/channel.types.d.ts +14 -27
- package/dist/channel/local/local-channel-runner.js +9 -4
- package/dist/channel/log-channels.js +4 -4
- package/dist/channel/serverless/serverless-channel-runner.js +17 -6
- package/dist/function/function-runner.d.ts +12 -0
- package/dist/function/function-runner.js +38 -0
- package/dist/{types → function}/functions.types.d.ts +12 -7
- package/dist/function/index.d.ts +2 -0
- package/dist/function/index.js +2 -0
- package/dist/handle-error.d.ts +1 -1
- package/dist/http/{http-route-runner.d.ts → http-runner.d.ts} +4 -4
- package/dist/http/{http-route-runner.js → http-runner.js} +81 -43
- package/dist/http/{http-routes.types.d.ts → http.types.d.ts} +41 -12
- package/dist/http/index.d.ts +2 -2
- package/dist/http/index.js +1 -1
- package/dist/http/log-http-routes.js +7 -5
- package/dist/http/pikku-fetch-http-request.d.ts +1 -1
- package/dist/http/pikku-fetch-http-response.d.ts +4 -2
- package/dist/http/pikku-fetch-http-response.js +64 -12
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/middleware/auth-apikey.js +5 -4
- package/dist/middleware/auth-bearer.js +5 -4
- package/dist/middleware/auth-cookie.js +10 -9
- package/dist/permissions.d.ts +2 -2
- package/dist/pikku-function.d.ts +1 -0
- package/dist/pikku-function.js +1 -0
- package/dist/pikku-state.d.ts +10 -5
- package/dist/pikku-state.js +7 -3
- package/dist/scheduler/scheduler-runner.d.ts +1 -1
- package/dist/scheduler/scheduler-runner.js +24 -6
- package/dist/scheduler/scheduler.types.d.ts +3 -2
- package/dist/services/content-service.d.ts +33 -37
- package/dist/types/core.types.d.ts +15 -1
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +15 -0
- package/lcov.info +1240 -965
- package/package.json +2 -1
- package/src/channel/channel-handler.ts +16 -78
- package/src/channel/channel-runner.ts +72 -17
- package/src/channel/channel.types.ts +25 -63
- package/src/channel/local/local-channel-runner.test.ts +10 -11
- package/src/channel/local/local-channel-runner.ts +20 -7
- package/src/channel/log-channels.ts +4 -4
- package/src/channel/serverless/serverless-channel-runner.ts +26 -15
- package/src/function/function-runner.ts +91 -0
- package/src/{types → function}/functions.types.ts +25 -11
- package/src/function/index.ts +2 -0
- package/src/handle-error.ts +1 -1
- package/src/http/{http-route-runner.test.ts → http-runner.test.ts} +29 -6
- package/src/http/{http-route-runner.ts → http-runner.ts} +106 -73
- package/src/http/{http-routes.types.ts → http.types.ts} +55 -16
- package/src/http/index.ts +2 -2
- package/src/http/log-http-routes.ts +7 -5
- package/src/http/pikku-fetch-http-request.ts +1 -5
- package/src/http/pikku-fetch-http-response.ts +67 -13
- package/src/index.ts +3 -2
- package/src/middleware/auth-apikey.ts +5 -4
- package/src/middleware/auth-bearer.ts +5 -4
- package/src/middleware/auth-cookie.ts +14 -9
- package/src/permissions.ts +2 -4
- package/src/pikku-function.ts +1 -0
- package/src/pikku-state.ts +25 -9
- package/src/scheduler/scheduler-runner.ts +31 -13
- package/src/scheduler/scheduler.types.ts +12 -8
- package/src/services/content-service.ts +36 -40
- package/src/types/core.types.ts +20 -1
- package/src/utils.ts +19 -0
- package/tsconfig.tsbuildinfo +1 -1
- /package/dist/{types → function}/functions.types.js +0 -0
- /package/dist/http/{http-routes.types.js → http.types.js} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"author": "yasser.fadl@gmail.com",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"exports": {
|
|
20
20
|
".": "./dist/index.js",
|
|
21
21
|
"./middleware": "./dist/middleware/index.js",
|
|
22
|
+
"./function": "./dist/function/index.js",
|
|
22
23
|
"./channel": "./dist/channel/index.js",
|
|
23
24
|
"./channel/local": "./dist/channel/local/index.js",
|
|
24
25
|
"./channel/serverless": "./dist/channel/serverless/index.js",
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
CoreSingletonServices,
|
|
3
2
|
CoreServices,
|
|
4
3
|
JSONValue,
|
|
5
4
|
CoreUserSession,
|
|
@@ -9,18 +8,15 @@ import {
|
|
|
9
8
|
CoreAPIChannel,
|
|
10
9
|
PikkuChannelHandler,
|
|
11
10
|
} from './channel.types.js'
|
|
12
|
-
import { verifyPermissions } from '../permissions.js'
|
|
13
11
|
import { pikkuState } from '../pikku-state.js'
|
|
12
|
+
import { runPikkuFunc } from '../function/function-runner.js'
|
|
14
13
|
|
|
15
14
|
const getRouteMeta = (
|
|
16
15
|
channelName: string,
|
|
17
16
|
routingProperty?: string,
|
|
18
17
|
routerValue?: string
|
|
19
18
|
): ChannelMessageMeta => {
|
|
20
|
-
const
|
|
21
|
-
const channelMeta = channelsMeta.find(
|
|
22
|
-
(channelMeta) => channelMeta.name === channelName
|
|
23
|
-
)
|
|
19
|
+
const channelMeta = pikkuState('channel', 'meta')[channelName]
|
|
24
20
|
if (!channelMeta) {
|
|
25
21
|
throw new Error(`Channel ${channelName} not found`)
|
|
26
22
|
}
|
|
@@ -44,19 +40,6 @@ const getRouteMeta = (
|
|
|
44
40
|
return route
|
|
45
41
|
}
|
|
46
42
|
|
|
47
|
-
const validateSchema = (
|
|
48
|
-
logger: CoreSingletonServices['logger'],
|
|
49
|
-
data: JSONValue,
|
|
50
|
-
channelRoute: ChannelMessageMeta
|
|
51
|
-
) => {
|
|
52
|
-
const schemaNames = channelRoute.inputs
|
|
53
|
-
if (schemaNames) {
|
|
54
|
-
// TODO
|
|
55
|
-
// loadSchema(schemaNames, logger)
|
|
56
|
-
// validateJson(schemaNames, data)
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
43
|
const validateAuth = (
|
|
61
44
|
requiresSession: boolean,
|
|
62
45
|
session: CoreUserSession | undefined,
|
|
@@ -75,40 +58,6 @@ const validateAuth = (
|
|
|
75
58
|
return true
|
|
76
59
|
}
|
|
77
60
|
|
|
78
|
-
const validatePermissions = async (
|
|
79
|
-
services: CoreServices,
|
|
80
|
-
session: CoreUserSession | undefined,
|
|
81
|
-
onMessage: any,
|
|
82
|
-
data: unknown
|
|
83
|
-
) => {
|
|
84
|
-
const permissions =
|
|
85
|
-
typeof onMessage === 'function' ? {} : onMessage.permissions
|
|
86
|
-
return await verifyPermissions(permissions, services, data, session)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const runFunction = async (
|
|
90
|
-
services: CoreServices,
|
|
91
|
-
channelHandler: PikkuChannelHandler,
|
|
92
|
-
channelMessageMeta: ChannelMessageMeta,
|
|
93
|
-
session: CoreUserSession | undefined,
|
|
94
|
-
onMessage: any,
|
|
95
|
-
data: unknown
|
|
96
|
-
) => {
|
|
97
|
-
const func: any = typeof onMessage === 'function' ? onMessage : onMessage.func
|
|
98
|
-
if (channelMessageMeta.type?.toLowerCase().includes('function')) {
|
|
99
|
-
return await func(
|
|
100
|
-
{
|
|
101
|
-
...services,
|
|
102
|
-
channel: channelHandler.getChannel(),
|
|
103
|
-
},
|
|
104
|
-
data,
|
|
105
|
-
session
|
|
106
|
-
)
|
|
107
|
-
} else {
|
|
108
|
-
return await func(services, channelHandler.getChannel(), data)
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
61
|
export const processMessageHandlers = (
|
|
113
62
|
services: CoreServices,
|
|
114
63
|
session: CoreUserSession | undefined,
|
|
@@ -137,36 +86,25 @@ export const processMessageHandlers = (
|
|
|
137
86
|
return
|
|
138
87
|
}
|
|
139
88
|
|
|
140
|
-
const
|
|
89
|
+
const { pikkuFuncName } = getRouteMeta(
|
|
141
90
|
channelConfig.name,
|
|
142
91
|
routingProperty,
|
|
143
92
|
routerValue
|
|
144
93
|
)
|
|
145
94
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const hasPermission = await validatePermissions(
|
|
151
|
-
services,
|
|
152
|
-
channelHandler,
|
|
153
|
-
onMessage,
|
|
154
|
-
data
|
|
155
|
-
)
|
|
156
|
-
if (!hasPermission) {
|
|
157
|
-
logger.error(
|
|
158
|
-
`Channel ${channelConfig.name} requires permissions for ${routingProperty || 'default message route'}`
|
|
159
|
-
)
|
|
160
|
-
}
|
|
95
|
+
const permissions =
|
|
96
|
+
typeof onMessage === 'function' ? {} : onMessage.permissions
|
|
161
97
|
|
|
162
|
-
return await
|
|
163
|
-
services,
|
|
164
|
-
|
|
165
|
-
|
|
98
|
+
return await runPikkuFunc(pikkuFuncName, {
|
|
99
|
+
singletonServices: services,
|
|
100
|
+
getAllServices: () => ({
|
|
101
|
+
...services,
|
|
102
|
+
channel: channelHandler.getChannel(),
|
|
103
|
+
}),
|
|
104
|
+
data,
|
|
166
105
|
session,
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
)
|
|
106
|
+
permissions,
|
|
107
|
+
})
|
|
170
108
|
}
|
|
171
109
|
|
|
172
110
|
const onMessage = async (rawData): Promise<unknown> => {
|
|
@@ -215,9 +153,9 @@ export const processMessageHandlers = (
|
|
|
215
153
|
|
|
216
154
|
if (!processed) {
|
|
217
155
|
logger.error(
|
|
218
|
-
`No handler found for message in channel ${channelConfig.name} for ${rawData}`
|
|
156
|
+
`No handler found for message in channel ${channelConfig.name} for ${JSON.stringify(rawData)}`
|
|
219
157
|
)
|
|
220
|
-
logger.error(`Channel ${channelConfig}`)
|
|
158
|
+
logger.error(`Channel ${JSON.stringify(channelConfig)}`)
|
|
221
159
|
}
|
|
222
160
|
|
|
223
161
|
return result
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { NotFoundError } from '../errors/errors.js'
|
|
2
|
+
import { addFunction } from '../function/function-runner.js'
|
|
2
3
|
import { pikkuState } from '../pikku-state.js'
|
|
3
4
|
import { coerceTopLevelDataFromSchema, validateSchema } from '../schema.js'
|
|
4
5
|
import { UserSessionService } from '../services/user-session-service.js'
|
|
5
6
|
import {
|
|
7
|
+
ChannelMeta,
|
|
6
8
|
CoreAPIChannel,
|
|
7
9
|
RunChannelOptions,
|
|
8
10
|
RunChannelParams,
|
|
9
11
|
} from './channel.types.js'
|
|
10
12
|
import { match } from 'path-to-regexp'
|
|
11
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
|
+
*/
|
|
12
18
|
export const addChannel = <
|
|
13
19
|
In,
|
|
14
20
|
Channel extends string,
|
|
@@ -16,21 +22,68 @@ export const addChannel = <
|
|
|
16
22
|
ChannelFunctionSessionless,
|
|
17
23
|
APIPermission,
|
|
18
24
|
>(
|
|
19
|
-
channel: CoreAPIChannel<
|
|
20
|
-
In,
|
|
21
|
-
Channel,
|
|
22
|
-
ChannelFunction,
|
|
23
|
-
ChannelFunctionSessionless,
|
|
24
|
-
APIPermission
|
|
25
|
-
>
|
|
25
|
+
channel: CoreAPIChannel<In, Channel, ChannelFunction, APIPermission>
|
|
26
26
|
) => {
|
|
27
|
-
|
|
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)
|
|
28
81
|
}
|
|
29
82
|
|
|
30
|
-
|
|
83
|
+
const getMatchingChannelConfig = (request: string) => {
|
|
31
84
|
const channels = pikkuState('channel', 'channels')
|
|
32
85
|
const channelsMeta = pikkuState('channel', 'meta')
|
|
33
|
-
for (const channelConfig of channels) {
|
|
86
|
+
for (const channelConfig of channels.values()) {
|
|
34
87
|
const cleanedRoute = channelConfig.route.replace(/^\/\//, '/')
|
|
35
88
|
const cleanedRequest = request.replace(/^\/\//, '/')
|
|
36
89
|
const matchFunc = match(cleanedRoute, {
|
|
@@ -38,18 +91,19 @@ export const getMatchingChannelConfig = (request: string) => {
|
|
|
38
91
|
})
|
|
39
92
|
const matchedPath = matchFunc(cleanedRequest)
|
|
40
93
|
if (matchedPath) {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
94
|
+
const channelMeta = channelsMeta[channelConfig.name]
|
|
95
|
+
if (!channelMeta) {
|
|
96
|
+
throw new Error(`Channel ${channelConfig.name} not found in metadata`)
|
|
97
|
+
}
|
|
44
98
|
return {
|
|
45
99
|
matchedPath,
|
|
46
100
|
params: matchedPath.params,
|
|
47
101
|
channelConfig,
|
|
48
|
-
schemaName,
|
|
102
|
+
schemaName: channelMeta.input,
|
|
103
|
+
meta: channelMeta,
|
|
49
104
|
}
|
|
50
105
|
}
|
|
51
106
|
}
|
|
52
|
-
|
|
53
107
|
return null
|
|
54
108
|
}
|
|
55
109
|
|
|
@@ -64,6 +118,7 @@ export const openChannel = async ({
|
|
|
64
118
|
} & RunChannelOptions): Promise<{
|
|
65
119
|
openingData: unknown
|
|
66
120
|
channelConfig: CoreAPIChannel<unknown, any>
|
|
121
|
+
meta: ChannelMeta
|
|
67
122
|
}> => {
|
|
68
123
|
const matchingChannel = getMatchingChannelConfig(route)
|
|
69
124
|
if (!matchingChannel) {
|
|
@@ -71,7 +126,7 @@ export const openChannel = async ({
|
|
|
71
126
|
throw new NotFoundError(`Channel not found: ${route}`)
|
|
72
127
|
}
|
|
73
128
|
|
|
74
|
-
const { params, channelConfig, schemaName } = matchingChannel
|
|
129
|
+
const { params, channelConfig, schemaName, meta } = matchingChannel
|
|
75
130
|
|
|
76
131
|
const requiresSession = channelConfig.auth !== false
|
|
77
132
|
request?.setParams(params)
|
|
@@ -94,5 +149,5 @@ export const openChannel = async ({
|
|
|
94
149
|
)
|
|
95
150
|
}
|
|
96
151
|
|
|
97
|
-
return { openingData, channelConfig }
|
|
152
|
+
return { openingData, channelConfig, meta }
|
|
98
153
|
}
|
|
@@ -3,15 +3,18 @@ import {
|
|
|
3
3
|
HTTPFunctionMetaInputTypes,
|
|
4
4
|
PikkuHTTPRequest,
|
|
5
5
|
PikkuHTTPResponse,
|
|
6
|
-
} from '../http/http
|
|
6
|
+
} from '../http/http.types.js'
|
|
7
7
|
import {
|
|
8
8
|
APIDocs,
|
|
9
|
-
CoreServices,
|
|
10
9
|
CoreSingletonServices,
|
|
11
10
|
CreateSessionServices,
|
|
12
11
|
PikkuMiddleware,
|
|
13
12
|
} from '../types/core.types.js'
|
|
14
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
CoreAPIFunction,
|
|
15
|
+
CoreAPIFunctionSessionless,
|
|
16
|
+
CoreAPIPermission,
|
|
17
|
+
} from '../function/functions.types.js'
|
|
15
18
|
|
|
16
19
|
export type RunChannelOptions = Partial<{
|
|
17
20
|
skipUserSession: boolean
|
|
@@ -30,10 +33,10 @@ export type RunChannelParams<ChannelData> = {
|
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
export interface ChannelMessageMeta {
|
|
36
|
+
pikkuFuncName: string
|
|
33
37
|
inputs: string[] | null
|
|
34
38
|
outputs: string[] | null
|
|
35
39
|
docs?: APIDocs
|
|
36
|
-
type: null | string
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
export interface ChannelMeta {
|
|
@@ -44,88 +47,49 @@ export interface ChannelMeta {
|
|
|
44
47
|
input: string | null
|
|
45
48
|
inputTypes?: HTTPFunctionMetaInputTypes
|
|
46
49
|
connect: boolean
|
|
50
|
+
connectPikkuFuncName: string | null
|
|
47
51
|
disconnect: boolean
|
|
52
|
+
disconnectPikkuFuncName: string | null
|
|
48
53
|
message: ChannelMessageMeta | null
|
|
49
54
|
messageRoutes: Record<string, Record<string, ChannelMessageMeta>>
|
|
50
55
|
docs?: APIDocs
|
|
51
56
|
tags?: string[]
|
|
52
57
|
}
|
|
53
58
|
|
|
54
|
-
export type ChannelsMeta = ChannelMeta
|
|
55
|
-
|
|
56
|
-
export type CoreChannelConnection<
|
|
57
|
-
ChannelData,
|
|
58
|
-
Out = unknown,
|
|
59
|
-
Services extends CoreServices = CoreServices,
|
|
60
|
-
> = (
|
|
61
|
-
services: Services,
|
|
62
|
-
channel: PikkuChannel<ChannelData, Out>
|
|
63
|
-
) => Promise<void>
|
|
64
|
-
|
|
65
|
-
export type CoreChannelDisconnection<
|
|
66
|
-
ChannelData,
|
|
67
|
-
Services extends CoreServices = CoreServices,
|
|
68
|
-
> = (
|
|
69
|
-
services: Services,
|
|
70
|
-
channel: PikkuChannel<ChannelData, never>
|
|
71
|
-
) => Promise<void>
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Represents a core channel function that performs an operation using core services and a user session.
|
|
75
|
-
*
|
|
76
|
-
* @template In - The input type.
|
|
77
|
-
* @template Services - The services type, defaults to `CoreServices`.
|
|
78
|
-
* @template Session - The session type, defaults to `CoreUserSession`.
|
|
79
|
-
*/
|
|
80
|
-
export type CoreChannelMessage<
|
|
81
|
-
In,
|
|
82
|
-
Out,
|
|
83
|
-
ChannelData,
|
|
84
|
-
Services extends CoreServices = CoreServices,
|
|
85
|
-
> = (
|
|
86
|
-
services: Services,
|
|
87
|
-
channel: PikkuChannel<ChannelData, Out>,
|
|
88
|
-
data: In
|
|
89
|
-
) => Promise<void | Out>
|
|
90
|
-
|
|
91
|
-
export type CoreAPIChannelMessage<
|
|
92
|
-
ChannelFunctionMessage = CoreChannelMessage<unknown, unknown, unknown>,
|
|
93
|
-
> = {
|
|
94
|
-
func: ChannelFunctionMessage
|
|
95
|
-
route: string
|
|
96
|
-
}
|
|
59
|
+
export type ChannelsMeta = Record<string, ChannelMeta>
|
|
97
60
|
|
|
98
61
|
export type CoreAPIChannel<
|
|
99
62
|
ChannelData,
|
|
100
63
|
Channel extends string,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
| CoreAPIFunction<
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
| CoreAPIFunction<unknown, unknown
|
|
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>,
|
|
109
73
|
APIPermission = CoreAPIPermission<ChannelData>,
|
|
110
74
|
> = {
|
|
111
75
|
name: string
|
|
112
76
|
route: Channel
|
|
113
|
-
onConnect?:
|
|
114
|
-
onDisconnect?:
|
|
77
|
+
onConnect?: ChannelConnect
|
|
78
|
+
onDisconnect?: ChannelDisconnect
|
|
115
79
|
onMessage?:
|
|
116
80
|
| {
|
|
117
|
-
func:
|
|
81
|
+
func: ChannelFunctionMessage
|
|
118
82
|
permissions?: Record<string, APIPermission[] | APIPermission>
|
|
119
83
|
auth?: boolean
|
|
120
84
|
}
|
|
121
|
-
|
|
|
85
|
+
| ChannelFunctionMessage
|
|
122
86
|
onMessageRoute?: Record<
|
|
123
87
|
string,
|
|
124
88
|
Record<
|
|
125
89
|
string,
|
|
126
|
-
|
|
|
90
|
+
| ChannelFunctionMessage
|
|
127
91
|
| {
|
|
128
|
-
func:
|
|
92
|
+
func: ChannelFunctionMessage
|
|
129
93
|
permissions?: Record<string, APIPermission[] | APIPermission>
|
|
130
94
|
auth?: boolean
|
|
131
95
|
}
|
|
@@ -143,8 +107,6 @@ export type CoreAPIChannel<
|
|
|
143
107
|
tags?: string[]
|
|
144
108
|
}
|
|
145
109
|
|
|
146
|
-
export type CoreAPIChannels = CoreAPIChannel<any, string>[]
|
|
147
|
-
|
|
148
110
|
export interface PikkuChannel<OpeningData, Out> {
|
|
149
111
|
// The channel identifier
|
|
150
112
|
channelId: string
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { test, beforeEach, afterEach } from 'node:test'
|
|
2
2
|
import * as assert from 'node:assert/strict'
|
|
3
3
|
import { runLocalChannel } from './local-channel-runner.js'
|
|
4
|
-
import { resetPikkuState } from '../../pikku-state.js'
|
|
4
|
+
import { pikkuState, resetPikkuState } from '../../pikku-state.js'
|
|
5
5
|
import { addChannel } from '../channel-runner.js'
|
|
6
6
|
import {
|
|
7
7
|
HTTPMethod,
|
|
8
8
|
PikkuHTTPRequest,
|
|
9
9
|
PikkuHTTPResponse,
|
|
10
10
|
PikkuQuery,
|
|
11
|
-
} from '../../http/http
|
|
11
|
+
} from '../../http/http.types.js'
|
|
12
12
|
import { SerializeOptions } from 'cookie'
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -126,22 +126,21 @@ test('runChannel should return undefined and 404 if no matching channel is found
|
|
|
126
126
|
|
|
127
127
|
test('runChannel should return a channel handler if channel matches and no auth required', async () => {
|
|
128
128
|
resetPikkuState()
|
|
129
|
+
|
|
130
|
+
pikkuState('channel', 'meta', {
|
|
131
|
+
test: {
|
|
132
|
+
name: 'test',
|
|
133
|
+
route: '/test-channel',
|
|
134
|
+
},
|
|
135
|
+
} as any)
|
|
129
136
|
addChannel({
|
|
130
137
|
name: 'test',
|
|
131
138
|
route: '/test-channel',
|
|
132
139
|
auth: false,
|
|
133
140
|
})
|
|
134
141
|
|
|
135
|
-
// Provide a fake channelPermissionService if needed
|
|
136
|
-
const singletonServicesWithPerm = {
|
|
137
|
-
...mockSingletonServices,
|
|
138
|
-
channelPermissionService: {
|
|
139
|
-
verifyChannelAccess: async () => {},
|
|
140
|
-
},
|
|
141
|
-
}
|
|
142
|
-
|
|
143
142
|
const result = await runLocalChannel({
|
|
144
|
-
singletonServices:
|
|
143
|
+
singletonServices: mockSingletonServices,
|
|
145
144
|
channelId: 'test-channel-id',
|
|
146
145
|
request: new PikkuMockRequest('/test-channel', 'get'),
|
|
147
146
|
response: new PikkuMockResponse(),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { openChannel } from '../channel-runner.js'
|
|
2
|
-
import { createHTTPInteraction } from '../../http/http-
|
|
2
|
+
import { createHTTPInteraction } from '../../http/http-runner.js'
|
|
3
3
|
import { closeSessionServices } from '../../utils.js'
|
|
4
4
|
import { processMessageHandlers } from '../channel-handler.js'
|
|
5
5
|
import {
|
|
@@ -12,7 +12,8 @@ import { SessionServices } from '../../types/core.types.js'
|
|
|
12
12
|
import { handleError } from '../../handle-error.js'
|
|
13
13
|
import { runMiddleware } from '../../middleware-runner.js'
|
|
14
14
|
import { PikkuUserSessionService } from '../../services/user-session-service.js'
|
|
15
|
-
import { PikkuHTTP } from '../../http/http
|
|
15
|
+
import { PikkuHTTP } from '../../http/http.types.js'
|
|
16
|
+
import { runPikkuFuncDirectly } from '../../function/function-runner.js'
|
|
16
17
|
|
|
17
18
|
export const runLocalChannel = async ({
|
|
18
19
|
singletonServices,
|
|
@@ -42,7 +43,7 @@ export const runLocalChannel = async ({
|
|
|
42
43
|
|
|
43
44
|
const main = async () => {
|
|
44
45
|
try {
|
|
45
|
-
const { openingData, channelConfig } = await openChannel({
|
|
46
|
+
const { openingData, channelConfig, meta } = await openChannel({
|
|
46
47
|
channelId,
|
|
47
48
|
createSessionServices,
|
|
48
49
|
respondWith404,
|
|
@@ -77,11 +78,23 @@ export const runLocalChannel = async ({
|
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
channelHandler.registerOnOpen(() => {
|
|
80
|
-
channelConfig.onConnect
|
|
81
|
+
if (channelConfig.onConnect && meta.connectPikkuFuncName) {
|
|
82
|
+
runPikkuFuncDirectly(
|
|
83
|
+
meta.connectPikkuFuncName,
|
|
84
|
+
{ ...allServices, channel },
|
|
85
|
+
openingData
|
|
86
|
+
)
|
|
87
|
+
}
|
|
81
88
|
})
|
|
82
89
|
|
|
83
90
|
channelHandler.registerOnClose(async () => {
|
|
84
|
-
channelConfig.onDisconnect
|
|
91
|
+
if (channelConfig.onDisconnect && meta.disconnectPikkuFuncName) {
|
|
92
|
+
runPikkuFuncDirectly(
|
|
93
|
+
meta.disconnectPikkuFuncName,
|
|
94
|
+
{ ...allServices, channel },
|
|
95
|
+
openingData
|
|
96
|
+
)
|
|
97
|
+
}
|
|
85
98
|
if (sessionServices) {
|
|
86
99
|
await closeSessionServices(singletonServices.logger, sessionServices)
|
|
87
100
|
}
|
|
@@ -91,7 +104,7 @@ export const runLocalChannel = async ({
|
|
|
91
104
|
processMessageHandlers(
|
|
92
105
|
allServices,
|
|
93
106
|
session,
|
|
94
|
-
channelConfig,
|
|
107
|
+
channelConfig as any,
|
|
95
108
|
channelHandler
|
|
96
109
|
)
|
|
97
110
|
)
|
|
@@ -122,5 +135,5 @@ export const runLocalChannel = async ({
|
|
|
122
135
|
main
|
|
123
136
|
)
|
|
124
137
|
|
|
125
|
-
return channelHandler
|
|
138
|
+
return channelHandler
|
|
126
139
|
}
|
|
@@ -7,14 +7,14 @@ import { Logger } from '../services/index.js'
|
|
|
7
7
|
*/
|
|
8
8
|
export const logChannels = (logger: Logger) => {
|
|
9
9
|
const channels = pikkuState('channel', 'channels')
|
|
10
|
-
if (channels.
|
|
10
|
+
if (channels.size === 0) {
|
|
11
11
|
logger.info('No channels added')
|
|
12
12
|
return
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
let scheduledChannels = 'Channels:'
|
|
16
|
-
|
|
17
|
-
scheduledChannels += `\n\t- ${name} at ${route}`
|
|
18
|
-
}
|
|
16
|
+
channels.forEach((channel) => {
|
|
17
|
+
scheduledChannels += `\n\t- ${channel.name} at ${channel.route}`
|
|
18
|
+
})
|
|
19
19
|
logger.info(scheduledChannels)
|
|
20
20
|
}
|
|
@@ -8,14 +8,15 @@ import type {
|
|
|
8
8
|
RunChannelParams,
|
|
9
9
|
PikkuChannelHandlerFactory,
|
|
10
10
|
} from '../channel.types.js'
|
|
11
|
-
import { createHTTPInteraction } from '../../http/http-
|
|
11
|
+
import { createHTTPInteraction } from '../../http/http-runner.js'
|
|
12
12
|
import { ChannelStore } from '../channel-store.js'
|
|
13
13
|
import { handleError } from '../../handle-error.js'
|
|
14
14
|
import { PikkuUserSessionService } from '../../services/user-session-service.js'
|
|
15
15
|
import { runMiddleware } from '../../middleware-runner.js'
|
|
16
16
|
import { pikkuState } from '../../pikku-state.js'
|
|
17
17
|
import { PikkuFetchHTTPRequest } from '../../http/pikku-fetch-http-request.js'
|
|
18
|
-
import { PikkuHTTP } from '../../http/http
|
|
18
|
+
import { PikkuHTTP } from '../../http/http.types.js'
|
|
19
|
+
import { runPikkuFuncDirectly } from '../../function/function-runner.js'
|
|
19
20
|
|
|
20
21
|
export interface RunServerlessChannelParams<ChannelData>
|
|
21
22
|
extends RunChannelParams<ChannelData> {
|
|
@@ -36,12 +37,15 @@ const getVariablesForChannel = ({
|
|
|
36
37
|
openingData?: unknown
|
|
37
38
|
}) => {
|
|
38
39
|
const channels = pikkuState('channel', 'channels')
|
|
39
|
-
const channelConfig = channels
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
const channelConfig = channels[channelName]
|
|
41
|
+
const channelsMeta = pikkuState('channel', 'meta')
|
|
42
|
+
const meta = channelsMeta[channelName]
|
|
42
43
|
if (!channelConfig) {
|
|
43
44
|
throw new Error(`Channel not found: ${channelName}`)
|
|
44
45
|
}
|
|
46
|
+
if (!meta) {
|
|
47
|
+
throw new Error(`Channel meta not found: ${channelName}`)
|
|
48
|
+
}
|
|
45
49
|
const channelHandler = channelHandlerFactory(
|
|
46
50
|
channelId,
|
|
47
51
|
channelConfig.name,
|
|
@@ -51,6 +55,7 @@ const getVariablesForChannel = ({
|
|
|
51
55
|
channelConfig,
|
|
52
56
|
channelHandler,
|
|
53
57
|
channel: channelHandler.getChannel(),
|
|
58
|
+
meta,
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
|
|
@@ -80,7 +85,7 @@ export const runChannelConnect = async ({
|
|
|
80
85
|
|
|
81
86
|
const userSession = new PikkuUserSessionService(channelStore, channelId)
|
|
82
87
|
|
|
83
|
-
const { channelConfig, openingData } = await openChannel({
|
|
88
|
+
const { channelConfig, openingData, meta } = await openChannel({
|
|
84
89
|
channelId,
|
|
85
90
|
createSessionServices,
|
|
86
91
|
request,
|
|
@@ -110,10 +115,13 @@ export const runChannelConnect = async ({
|
|
|
110
115
|
await userSession.get()
|
|
111
116
|
)
|
|
112
117
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
118
|
+
if (channelConfig.onConnect && meta.connectPikkuFuncName) {
|
|
119
|
+
await runPikkuFuncDirectly(
|
|
120
|
+
meta.connectPikkuFuncName,
|
|
121
|
+
{ ...singletonServices, ...sessionServices, channel },
|
|
122
|
+
openingData
|
|
123
|
+
)
|
|
124
|
+
}
|
|
117
125
|
http?.response?.status(101)
|
|
118
126
|
} catch (e: any) {
|
|
119
127
|
handleError(
|
|
@@ -150,7 +158,7 @@ export const runChannelDisconnect = async ({
|
|
|
150
158
|
let sessionServices: SessionServices | undefined
|
|
151
159
|
const { openingData, channelName, session } =
|
|
152
160
|
await params.channelStore.getChannelAndSession(params.channelId)
|
|
153
|
-
const { channel, channelConfig } = getVariablesForChannel({
|
|
161
|
+
const { channel, channelConfig, meta } = getVariablesForChannel({
|
|
154
162
|
...params,
|
|
155
163
|
openingData,
|
|
156
164
|
channelName,
|
|
@@ -162,10 +170,13 @@ export const runChannelDisconnect = async ({
|
|
|
162
170
|
session
|
|
163
171
|
)
|
|
164
172
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
173
|
+
if (channelConfig.onDisconnect && meta.disconnectPikkuFuncName) {
|
|
174
|
+
await runPikkuFuncDirectly(
|
|
175
|
+
meta.disconnectPikkuFuncName,
|
|
176
|
+
{ ...singletonServices, ...sessionServices, channel },
|
|
177
|
+
undefined
|
|
178
|
+
)
|
|
179
|
+
}
|
|
169
180
|
await params.channelStore.removeChannels([channel.channelId])
|
|
170
181
|
if (sessionServices) {
|
|
171
182
|
await closeSessionServices(singletonServices.logger, sessionServices)
|