@pikku/core 0.6.26 → 0.7.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/CHANGELOG.md +17 -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/function/functions.types.d.ts +37 -0
- package/dist/function/functions.types.js +1 -0
- 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 +3 -3
- package/dist/http/http-route-runner.js +60 -35
- package/dist/http/http-routes.types.d.ts +38 -7
- package/dist/http/http-runner.d.ts +90 -0
- package/dist/http/http-runner.js +336 -0
- package/dist/http/http.types.d.ts +177 -0
- package/dist/http/http.types.js +1 -0
- 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/http/pikku-http-request.d.ts +56 -0
- package/dist/http/pikku-http-request.js +95 -0
- package/dist/http/pikku-http-response.d.ts +13 -0
- package/dist/http/pikku-http-response.js +60 -0
- 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/parse-relative-time-offset.d.ts +12 -0
- package/dist/parse-relative-time-offset.js +20 -0
- package/dist/permissions.d.ts +2 -2
- package/dist/pikku-func.d.ts +12 -0
- package/dist/pikku-func.js +39 -0
- 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 +34 -36
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/local-content.d.ts +4 -2
- package/dist/services/local-content.js +19 -9
- package/dist/types/core.types.d.ts +15 -1
- package/dist/types/functions.types.d.ts +11 -6
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +15 -0
- package/package.json +2 -1
- package/lcov.info +0 -4310
- package/src/channel/channel-handler.ts +0 -227
- package/src/channel/channel-runner.ts +0 -98
- package/src/channel/channel-store.ts +0 -29
- package/src/channel/channel.types.ts +0 -171
- package/src/channel/eventhub-service.ts +0 -30
- package/src/channel/eventhub-store.ts +0 -8
- package/src/channel/index.ts +0 -8
- package/src/channel/local/index.ts +0 -3
- package/src/channel/local/local-channel-handler.ts +0 -50
- package/src/channel/local/local-channel-runner.test.ts +0 -158
- package/src/channel/local/local-channel-runner.ts +0 -126
- package/src/channel/local/local-eventhub-service.test.ts +0 -95
- package/src/channel/local/local-eventhub-service.ts +0 -95
- package/src/channel/log-channels.ts +0 -20
- package/src/channel/pikku-abstract-channel-handler.test.ts +0 -52
- package/src/channel/pikku-abstract-channel-handler.ts +0 -38
- package/src/channel/serverless/index.ts +0 -1
- package/src/channel/serverless/serverless-channel-runner.ts +0 -209
- package/src/errors/error-handler.ts +0 -61
- package/src/errors/error.test.ts +0 -54
- package/src/errors/errors.ts +0 -339
- package/src/errors/index.ts +0 -2
- package/src/handle-error.ts +0 -73
- package/src/http/http-route-runner.test.ts +0 -117
- package/src/http/http-route-runner.ts +0 -460
- package/src/http/http-routes.types.ts +0 -211
- package/src/http/incomingmessage-to-request-convertor.ts +0 -0
- package/src/http/index.ts +0 -8
- package/src/http/log-http-routes.ts +0 -20
- package/src/http/pikku-fetch-http-request.test.ts +0 -237
- package/src/http/pikku-fetch-http-request.ts +0 -167
- package/src/http/pikku-fetch-http-response.test.ts +0 -82
- package/src/http/pikku-fetch-http-response.ts +0 -84
- package/src/index.ts +0 -19
- package/src/middleware/auth-apikey.ts +0 -65
- package/src/middleware/auth-bearer.ts +0 -65
- package/src/middleware/auth-cookie.ts +0 -98
- package/src/middleware/index.ts +0 -3
- package/src/middleware/timeout.ts +0 -13
- package/src/middleware-runner.ts +0 -43
- package/src/permissions.test.ts +0 -58
- package/src/permissions.ts +0 -45
- package/src/pikku-request.ts +0 -23
- package/src/pikku-response.ts +0 -5
- package/src/pikku-state.ts +0 -71
- package/src/scheduler/index.ts +0 -5
- package/src/scheduler/log-schedulers.ts +0 -20
- package/src/scheduler/scheduler-runner.ts +0 -87
- package/src/scheduler/scheduler.types.ts +0 -29
- package/src/schema.test.ts +0 -57
- package/src/schema.ts +0 -99
- package/src/services/content-service.ts +0 -69
- package/src/services/index.ts +0 -17
- package/src/services/jwt-service.ts +0 -30
- package/src/services/local-content.ts +0 -91
- package/src/services/local-secrets.ts +0 -43
- package/src/services/local-variables.ts +0 -17
- package/src/services/logger-console.ts +0 -97
- package/src/services/logger.ts +0 -57
- package/src/services/schema-service.ts +0 -26
- package/src/services/secret-service.ts +0 -17
- package/src/services/user-session-service.ts +0 -53
- package/src/services/variables-service.ts +0 -6
- package/src/time-utils.test.ts +0 -56
- package/src/time-utils.ts +0 -32
- package/src/types/core.types.ts +0 -159
- package/src/types/functions.types.ts +0 -63
- package/src/utils.ts +0 -21
- package/tsconfig.json +0 -13
- package/tsconfig.tsbuildinfo +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,20 @@
|
|
|
1
|
-
## 0.
|
|
1
|
+
## 0.7.0
|
|
2
|
+
|
|
3
|
+
We now use the function first approach internally, which means first all the functions register, and then events call call them.
|
|
4
|
+
|
|
5
|
+
The main breaking changes for the end user are:
|
|
6
|
+
|
|
7
|
+
- We now declare functions using `pikkuFunc<In, Out>(async () => {})
|
|
8
|
+
- We renamed addRoute to addHTTPRoutes
|
|
9
|
+
|
|
10
|
+
We also removed all the different types of functions. Everything is now either an APIFunction of APIFunctionSessionless. The channel (eventHub or any other transport specific service) is now injected in the service itself.
|
|
11
|
+
|
|
12
|
+
## 0.6.27
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- 8658745: refactor: changing content service to use streams for performance benefits
|
|
17
|
+
- d0968d2: fix: fixing content uploads for s3
|
|
2
18
|
|
|
3
19
|
## 0.6.26
|
|
4
20
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { verifyPermissions } from '../permissions.js';
|
|
2
1
|
import { pikkuState } from '../pikku-state.js';
|
|
2
|
+
import { runPikkuFunc } from '../function/function-runner.js';
|
|
3
3
|
const getRouteMeta = (channelName, routingProperty, routerValue) => {
|
|
4
|
-
const
|
|
5
|
-
const channelMeta = channelsMeta.find((channelMeta) => channelMeta.name === channelName);
|
|
4
|
+
const channelMeta = pikkuState('channel', 'meta')[channelName];
|
|
6
5
|
if (!channelMeta) {
|
|
7
6
|
throw new Error(`Channel ${channelName} not found`);
|
|
8
7
|
}
|
|
@@ -21,14 +20,6 @@ const getRouteMeta = (channelName, routingProperty, routerValue) => {
|
|
|
21
20
|
}
|
|
22
21
|
return route;
|
|
23
22
|
};
|
|
24
|
-
const validateSchema = (logger, data, channelRoute) => {
|
|
25
|
-
const schemaNames = channelRoute.inputs;
|
|
26
|
-
if (schemaNames) {
|
|
27
|
-
// TODO
|
|
28
|
-
// loadSchema(schemaNames, logger)
|
|
29
|
-
// validateJson(schemaNames, data)
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
23
|
const validateAuth = (requiresSession, session, onMessage) => {
|
|
33
24
|
const auth = typeof onMessage === 'function'
|
|
34
25
|
? requiresSession
|
|
@@ -40,22 +31,6 @@ const validateAuth = (requiresSession, session, onMessage) => {
|
|
|
40
31
|
}
|
|
41
32
|
return true;
|
|
42
33
|
};
|
|
43
|
-
const validatePermissions = async (services, session, onMessage, data) => {
|
|
44
|
-
const permissions = typeof onMessage === 'function' ? {} : onMessage.permissions;
|
|
45
|
-
return await verifyPermissions(permissions, services, data, session);
|
|
46
|
-
};
|
|
47
|
-
const runFunction = async (services, channelHandler, channelMessageMeta, session, onMessage, data) => {
|
|
48
|
-
const func = typeof onMessage === 'function' ? onMessage : onMessage.func;
|
|
49
|
-
if (channelMessageMeta.type?.toLowerCase().includes('function')) {
|
|
50
|
-
return await func({
|
|
51
|
-
...services,
|
|
52
|
-
channel: channelHandler.getChannel(),
|
|
53
|
-
}, data, session);
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
return await func(services, channelHandler.getChannel(), data);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
34
|
export const processMessageHandlers = (services, session, channelConfig, channelHandler) => {
|
|
60
35
|
const logger = services.logger;
|
|
61
36
|
const requiresSession = channelConfig.auth !== false;
|
|
@@ -69,15 +44,18 @@ export const processMessageHandlers = (services, session, channelConfig, channel
|
|
|
69
44
|
channelHandler.getChannel().send(`Unauthorized for ${routeMessage}`);
|
|
70
45
|
return;
|
|
71
46
|
}
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
47
|
+
const { pikkuFuncName } = getRouteMeta(channelConfig.name, routingProperty, routerValue);
|
|
48
|
+
const permissions = typeof onMessage === 'function' ? {} : onMessage.permissions;
|
|
49
|
+
return await runPikkuFunc(pikkuFuncName, {
|
|
50
|
+
singletonServices: services,
|
|
51
|
+
getAllServices: () => ({
|
|
52
|
+
...services,
|
|
53
|
+
channel: channelHandler.getChannel(),
|
|
54
|
+
}),
|
|
55
|
+
data,
|
|
56
|
+
session,
|
|
57
|
+
permissions,
|
|
58
|
+
});
|
|
81
59
|
};
|
|
82
60
|
const onMessage = async (rawData) => {
|
|
83
61
|
let result;
|
|
@@ -111,8 +89,8 @@ export const processMessageHandlers = (services, session, channelConfig, channel
|
|
|
111
89
|
result = await processMessage(rawData, channelConfig.onMessage, session);
|
|
112
90
|
}
|
|
113
91
|
if (!processed) {
|
|
114
|
-
logger.error(`No handler found for message in channel ${channelConfig.name} for ${rawData}`);
|
|
115
|
-
logger.error(`Channel ${channelConfig}`);
|
|
92
|
+
logger.error(`No handler found for message in channel ${channelConfig.name} for ${JSON.stringify(rawData)}`);
|
|
93
|
+
logger.error(`Channel ${JSON.stringify(channelConfig)}`);
|
|
116
94
|
}
|
|
117
95
|
return result;
|
|
118
96
|
};
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { UserSessionService } from '../services/user-session-service.js';
|
|
2
|
-
import { CoreAPIChannel, RunChannelOptions, RunChannelParams } from './channel.types.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
schemaName: string | null | undefined;
|
|
9
|
-
} | null;
|
|
2
|
+
import { ChannelMeta, CoreAPIChannel, RunChannelOptions, RunChannelParams } from './channel.types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Adds a channel and registers all functions referenced in it using the
|
|
5
|
+
* function names already stored in the channel metadata
|
|
6
|
+
*/
|
|
7
|
+
export declare const addChannel: <In, Channel extends string, ChannelFunction, ChannelFunctionSessionless, APIPermission>(channel: CoreAPIChannel<In, Channel, ChannelFunction, APIPermission>) => void;
|
|
10
8
|
export declare const openChannel: ({ route, singletonServices, coerceDataFromSchema, request, }: Pick<CoreAPIChannel<unknown, string>, "route"> & RunChannelParams<unknown> & {
|
|
11
9
|
userSession: UserSessionService<any>;
|
|
12
10
|
} & RunChannelOptions) => Promise<{
|
|
13
11
|
openingData: unknown;
|
|
14
12
|
channelConfig: CoreAPIChannel<unknown, any>;
|
|
13
|
+
meta: ChannelMeta;
|
|
15
14
|
}>;
|
|
@@ -1,14 +1,61 @@
|
|
|
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 { match } from 'path-to-regexp';
|
|
6
|
+
/**
|
|
7
|
+
* Adds a channel and registers all functions referenced in it using the
|
|
8
|
+
* function names already stored in the channel metadata
|
|
9
|
+
*/
|
|
5
10
|
export const addChannel = (channel) => {
|
|
6
|
-
|
|
11
|
+
// Get the channel metadata
|
|
12
|
+
const channelsMeta = pikkuState('channel', 'meta');
|
|
13
|
+
const channelMeta = channelsMeta[channel.name];
|
|
14
|
+
if (!channelMeta) {
|
|
15
|
+
throw new Error(`Channel metadata not found for channel: ${channel.name}`);
|
|
16
|
+
}
|
|
17
|
+
pikkuState('channel', 'channels').set(channel.name, channel);
|
|
18
|
+
// Register onConnect function if provided
|
|
19
|
+
if (channel.onConnect && channelMeta.connectPikkuFuncName) {
|
|
20
|
+
addFunction(channelMeta.connectPikkuFuncName, channel.onConnect);
|
|
21
|
+
}
|
|
22
|
+
// Register onDisconnect function if provided
|
|
23
|
+
if (channel.onDisconnect && channelMeta.disconnectPikkuFuncName) {
|
|
24
|
+
addFunction(channelMeta.disconnectPikkuFuncName, channel.onDisconnect);
|
|
25
|
+
}
|
|
26
|
+
// Register onMessage function if provided
|
|
27
|
+
if (channel.onMessage && channelMeta.message?.pikkuFuncName) {
|
|
28
|
+
const messageFunc = typeof channel.onMessage === 'function'
|
|
29
|
+
? channel.onMessage
|
|
30
|
+
: channel.onMessage.func;
|
|
31
|
+
addFunction(channelMeta.message.pikkuFuncName, messageFunc);
|
|
32
|
+
}
|
|
33
|
+
// Register functions in onMessageRoute
|
|
34
|
+
if (channel.onMessageRoute && channelMeta.messageRoutes) {
|
|
35
|
+
// Iterate through each channel in onMessageRoute
|
|
36
|
+
Object.entries(channel.onMessageRoute).forEach(([channelKey, routes]) => {
|
|
37
|
+
const channelRoutes = channelMeta.messageRoutes[channelKey];
|
|
38
|
+
if (!channelRoutes)
|
|
39
|
+
return;
|
|
40
|
+
// Iterate through each route in the channel
|
|
41
|
+
Object.entries(routes).forEach(([routeKey, handler]) => {
|
|
42
|
+
const routeMeta = channelRoutes[routeKey];
|
|
43
|
+
if (!routeMeta)
|
|
44
|
+
return;
|
|
45
|
+
// Extract the function from the handler
|
|
46
|
+
const routeFunc = typeof handler === 'function' ? handler : handler.func;
|
|
47
|
+
// Register the function using the pikku name from metadata
|
|
48
|
+
addFunction(routeMeta.pikkuFuncName, routeFunc);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// Store the channel configuration
|
|
53
|
+
pikkuState('channel', 'channels').set(channel.name, channel);
|
|
7
54
|
};
|
|
8
|
-
|
|
55
|
+
const getMatchingChannelConfig = (request) => {
|
|
9
56
|
const channels = pikkuState('channel', 'channels');
|
|
10
57
|
const channelsMeta = pikkuState('channel', 'meta');
|
|
11
|
-
for (const channelConfig of channels) {
|
|
58
|
+
for (const channelConfig of channels.values()) {
|
|
12
59
|
const cleanedRoute = channelConfig.route.replace(/^\/\//, '/');
|
|
13
60
|
const cleanedRequest = request.replace(/^\/\//, '/');
|
|
14
61
|
const matchFunc = match(cleanedRoute, {
|
|
@@ -16,12 +63,16 @@ export const getMatchingChannelConfig = (request) => {
|
|
|
16
63
|
});
|
|
17
64
|
const matchedPath = matchFunc(cleanedRequest);
|
|
18
65
|
if (matchedPath) {
|
|
19
|
-
const
|
|
66
|
+
const channelMeta = channelsMeta[channelConfig.name];
|
|
67
|
+
if (!channelMeta) {
|
|
68
|
+
throw new Error(`Channel ${channelConfig.name} not found in metadata`);
|
|
69
|
+
}
|
|
20
70
|
return {
|
|
21
71
|
matchedPath,
|
|
22
72
|
params: matchedPath.params,
|
|
23
73
|
channelConfig,
|
|
24
|
-
schemaName,
|
|
74
|
+
schemaName: channelMeta.input,
|
|
75
|
+
meta: channelMeta
|
|
25
76
|
};
|
|
26
77
|
}
|
|
27
78
|
}
|
|
@@ -33,7 +84,7 @@ export const openChannel = async ({ route, singletonServices, coerceDataFromSche
|
|
|
33
84
|
singletonServices.logger.info(`Channel not found: ${route}`);
|
|
34
85
|
throw new NotFoundError(`Channel not found: ${route}`);
|
|
35
86
|
}
|
|
36
|
-
const { params, channelConfig, schemaName } = matchingChannel;
|
|
87
|
+
const { params, channelConfig, schemaName, meta } = matchingChannel;
|
|
37
88
|
const requiresSession = channelConfig.auth !== false;
|
|
38
89
|
request?.setParams(params);
|
|
39
90
|
singletonServices.logger.info(`Matched channel: ${channelConfig.name} | route: ${channelConfig.route} | auth: ${requiresSession.toString()}`);
|
|
@@ -45,5 +96,5 @@ export const openChannel = async ({ route, singletonServices, coerceDataFromSche
|
|
|
45
96
|
}
|
|
46
97
|
await validateSchema(singletonServices.logger, singletonServices.schema, schemaName, openingData);
|
|
47
98
|
}
|
|
48
|
-
return { openingData, channelConfig };
|
|
99
|
+
return { openingData, channelConfig, meta };
|
|
49
100
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PikkuError } from '../errors/error-handler.js';
|
|
2
|
-
import { HTTPFunctionMetaInputTypes, PikkuHTTPRequest, PikkuHTTPResponse } from '../http/http
|
|
3
|
-
import { APIDocs,
|
|
4
|
-
import { CoreAPIFunction, CoreAPIPermission } from '../
|
|
2
|
+
import { HTTPFunctionMetaInputTypes, PikkuHTTPRequest, PikkuHTTPResponse } from '../http/http.types.js';
|
|
3
|
+
import { APIDocs, CoreSingletonServices, CreateSessionServices, PikkuMiddleware } from '../types/core.types.js';
|
|
4
|
+
import { CoreAPIFunction, CoreAPIFunctionSessionless, CoreAPIPermission } from '../function/functions.types.js';
|
|
5
5
|
export type RunChannelOptions = Partial<{
|
|
6
6
|
skipUserSession: boolean;
|
|
7
7
|
respondWith404: boolean;
|
|
@@ -17,10 +17,10 @@ export type RunChannelParams<ChannelData> = {
|
|
|
17
17
|
createSessionServices?: CreateSessionServices;
|
|
18
18
|
};
|
|
19
19
|
export interface ChannelMessageMeta {
|
|
20
|
+
pikkuFuncName: string;
|
|
20
21
|
inputs: string[] | null;
|
|
21
22
|
outputs: string[] | null;
|
|
22
23
|
docs?: APIDocs;
|
|
23
|
-
type: null | string;
|
|
24
24
|
}
|
|
25
25
|
export interface ChannelMeta {
|
|
26
26
|
name: string;
|
|
@@ -30,39 +30,27 @@ export interface ChannelMeta {
|
|
|
30
30
|
input: string | null;
|
|
31
31
|
inputTypes?: HTTPFunctionMetaInputTypes;
|
|
32
32
|
connect: boolean;
|
|
33
|
+
connectPikkuFuncName: string | null;
|
|
33
34
|
disconnect: boolean;
|
|
35
|
+
disconnectPikkuFuncName: string | null;
|
|
34
36
|
message: ChannelMessageMeta | null;
|
|
35
37
|
messageRoutes: Record<string, Record<string, ChannelMessageMeta>>;
|
|
36
38
|
docs?: APIDocs;
|
|
37
39
|
tags?: string[];
|
|
38
40
|
}
|
|
39
|
-
export type ChannelsMeta = ChannelMeta
|
|
40
|
-
export type
|
|
41
|
-
export type CoreChannelDisconnection<ChannelData, Services extends CoreServices = CoreServices> = (services: Services, channel: PikkuChannel<ChannelData, never>) => Promise<void>;
|
|
42
|
-
/**
|
|
43
|
-
* Represents a core channel function that performs an operation using core services and a user session.
|
|
44
|
-
*
|
|
45
|
-
* @template In - The input type.
|
|
46
|
-
* @template Services - The services type, defaults to `CoreServices`.
|
|
47
|
-
* @template Session - The session type, defaults to `CoreUserSession`.
|
|
48
|
-
*/
|
|
49
|
-
export type CoreChannelMessage<In, Out, ChannelData, Services extends CoreServices = CoreServices> = (services: Services, channel: PikkuChannel<ChannelData, Out>, data: In) => Promise<void | Out>;
|
|
50
|
-
export type CoreAPIChannelMessage<ChannelFunctionMessage = CoreChannelMessage<unknown, unknown, unknown>> = {
|
|
51
|
-
func: ChannelFunctionMessage;
|
|
52
|
-
route: string;
|
|
53
|
-
};
|
|
54
|
-
export type CoreAPIChannel<ChannelData, Channel extends string, ChannelFunctionConnection = CoreChannelConnection<ChannelData>, ChannelFunctionDisconnection = CoreChannelDisconnection<ChannelData>, ChannelFunctionDefaultMessage = CoreChannelMessage<unknown, unknown, unknown> | CoreAPIFunction<unknown, unknown>, ChannelFunctionMessageRoute = CoreChannelMessage<unknown, unknown, unknown> | CoreAPIFunction<unknown, unknown>, APIPermission = CoreAPIPermission<ChannelData>> = {
|
|
41
|
+
export type ChannelsMeta = Record<string, ChannelMeta>;
|
|
42
|
+
export type CoreAPIChannel<ChannelData, Channel extends string, ChannelConnect = CoreAPIFunction<void, unknown, ChannelData> | CoreAPIFunctionSessionless<void, unknown, ChannelData>, ChannelDisconnect = CoreAPIFunction<void, void, ChannelData> | CoreAPIFunctionSessionless<void, void, ChannelData>, ChannelFunctionMessage = CoreAPIFunction<unknown, unknown, ChannelData> | CoreAPIFunctionSessionless<unknown, unknown, ChannelData>, APIPermission = CoreAPIPermission<ChannelData>> = {
|
|
55
43
|
name: string;
|
|
56
44
|
route: Channel;
|
|
57
|
-
onConnect?:
|
|
58
|
-
onDisconnect?:
|
|
45
|
+
onConnect?: ChannelConnect;
|
|
46
|
+
onDisconnect?: ChannelDisconnect;
|
|
59
47
|
onMessage?: {
|
|
60
|
-
func:
|
|
48
|
+
func: ChannelFunctionMessage;
|
|
61
49
|
permissions?: Record<string, APIPermission[] | APIPermission>;
|
|
62
50
|
auth?: boolean;
|
|
63
|
-
} |
|
|
64
|
-
onMessageRoute?: Record<string, Record<string,
|
|
65
|
-
func:
|
|
51
|
+
} | ChannelFunctionMessage;
|
|
52
|
+
onMessageRoute?: Record<string, Record<string, ChannelFunctionMessage | {
|
|
53
|
+
func: ChannelFunctionMessage;
|
|
66
54
|
permissions?: Record<string, APIPermission[] | APIPermission>;
|
|
67
55
|
auth?: boolean;
|
|
68
56
|
}>>;
|
|
@@ -77,7 +65,6 @@ export type CoreAPIChannel<ChannelData, Channel extends string, ChannelFunctionC
|
|
|
77
65
|
}>;
|
|
78
66
|
tags?: string[];
|
|
79
67
|
};
|
|
80
|
-
export type CoreAPIChannels = CoreAPIChannel<any, string>[];
|
|
81
68
|
export interface PikkuChannel<OpeningData, Out> {
|
|
82
69
|
channelId: string;
|
|
83
70
|
openingData: OpeningData;
|
|
@@ -1,11 +1,12 @@
|
|
|
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 { PikkuLocalChannelHandler } from './local-channel-handler.js';
|
|
6
6
|
import { handleError } from '../../handle-error.js';
|
|
7
7
|
import { runMiddleware } from '../../middleware-runner.js';
|
|
8
8
|
import { PikkuUserSessionService } from '../../services/user-session-service.js';
|
|
9
|
+
import { runPikkuFuncDirectly } from '../../function/function-runner.js';
|
|
9
10
|
export const runLocalChannel = async ({ singletonServices, channelId, request, response, route, createSessionServices, skipUserSession = false, respondWith404 = true, coerceDataFromSchema = true, logWarningsForStatusCodes = [], bubbleErrors = false, }) => {
|
|
10
11
|
let sessionServices;
|
|
11
12
|
let channelHandler;
|
|
@@ -17,7 +18,7 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
|
|
|
17
18
|
}
|
|
18
19
|
const main = async () => {
|
|
19
20
|
try {
|
|
20
|
-
const { openingData, channelConfig } = await openChannel({
|
|
21
|
+
const { openingData, channelConfig, meta } = await openChannel({
|
|
21
22
|
channelId,
|
|
22
23
|
createSessionServices,
|
|
23
24
|
respondWith404,
|
|
@@ -41,10 +42,14 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
|
|
|
41
42
|
userSession: userSession,
|
|
42
43
|
};
|
|
43
44
|
channelHandler.registerOnOpen(() => {
|
|
44
|
-
channelConfig.onConnect
|
|
45
|
+
if (channelConfig.onConnect && meta.connectPikkuFuncName) {
|
|
46
|
+
runPikkuFuncDirectly(meta.connectPikkuFuncName, { ...allServices, channel }, openingData);
|
|
47
|
+
}
|
|
45
48
|
});
|
|
46
49
|
channelHandler.registerOnClose(async () => {
|
|
47
|
-
channelConfig.onDisconnect
|
|
50
|
+
if (channelConfig.onDisconnect && meta.disconnectPikkuFuncName) {
|
|
51
|
+
runPikkuFuncDirectly(meta.disconnectPikkuFuncName, { ...allServices, channel }, openingData);
|
|
52
|
+
}
|
|
48
53
|
if (sessionServices) {
|
|
49
54
|
await closeSessionServices(singletonServices.logger, sessionServices);
|
|
50
55
|
}
|
|
@@ -5,13 +5,13 @@ import { pikkuState } from '../pikku-state.js';
|
|
|
5
5
|
*/
|
|
6
6
|
export const logChannels = (logger) => {
|
|
7
7
|
const channels = pikkuState('channel', 'channels');
|
|
8
|
-
if (channels.
|
|
8
|
+
if (channels.size === 0) {
|
|
9
9
|
logger.info('No channels added');
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
12
|
let scheduledChannels = 'Channels:';
|
|
13
|
-
|
|
14
|
-
scheduledChannels += `\n\t- ${name} at ${route}`;
|
|
15
|
-
}
|
|
13
|
+
channels.forEach((channel) => {
|
|
14
|
+
scheduledChannels += `\n\t- ${channel.name} at ${channel.route}`;
|
|
15
|
+
});
|
|
16
16
|
logger.info(scheduledChannels);
|
|
17
17
|
};
|
|
@@ -1,23 +1,30 @@
|
|
|
1
1
|
import { closeSessionServices } from '../../utils.js';
|
|
2
2
|
import { processMessageHandlers } from '../channel-handler.js';
|
|
3
3
|
import { openChannel } from '../channel-runner.js';
|
|
4
|
-
import { createHTTPInteraction } from '../../http/http-
|
|
4
|
+
import { createHTTPInteraction } from '../../http/http-runner.js';
|
|
5
5
|
import { handleError } from '../../handle-error.js';
|
|
6
6
|
import { PikkuUserSessionService } from '../../services/user-session-service.js';
|
|
7
7
|
import { runMiddleware } from '../../middleware-runner.js';
|
|
8
8
|
import { pikkuState } from '../../pikku-state.js';
|
|
9
9
|
import { PikkuFetchHTTPRequest } from '../../http/pikku-fetch-http-request.js';
|
|
10
|
+
import { runPikkuFuncDirectly } from '../../function/function-runner.js';
|
|
10
11
|
const getVariablesForChannel = ({ channelId, channelName, channelHandlerFactory, openingData, }) => {
|
|
11
12
|
const channels = pikkuState('channel', 'channels');
|
|
12
|
-
const channelConfig = channels
|
|
13
|
+
const channelConfig = channels[channelName];
|
|
14
|
+
const channelsMeta = pikkuState('channel', 'meta');
|
|
15
|
+
const meta = channelsMeta[channelName];
|
|
13
16
|
if (!channelConfig) {
|
|
14
17
|
throw new Error(`Channel not found: ${channelName}`);
|
|
15
18
|
}
|
|
19
|
+
if (!meta) {
|
|
20
|
+
throw new Error(`Channel meta not found: ${channelName}`);
|
|
21
|
+
}
|
|
16
22
|
const channelHandler = channelHandlerFactory(channelId, channelConfig.name, openingData);
|
|
17
23
|
return {
|
|
18
24
|
channelConfig,
|
|
19
25
|
channelHandler,
|
|
20
26
|
channel: channelHandler.getChannel(),
|
|
27
|
+
meta
|
|
21
28
|
};
|
|
22
29
|
};
|
|
23
30
|
export const runChannelConnect = async ({ singletonServices, channelId, channelObject, request, response, route, createSessionServices, channelStore, channelHandlerFactory, coerceDataFromSchema = true, logWarningsForStatusCodes = [], respondWith404 = true, bubbleErrors = false, }) => {
|
|
@@ -27,7 +34,7 @@ export const runChannelConnect = async ({ singletonServices, channelId, channelO
|
|
|
27
34
|
http = createHTTPInteraction(new PikkuFetchHTTPRequest(request), response);
|
|
28
35
|
}
|
|
29
36
|
const userSession = new PikkuUserSessionService(channelStore, channelId);
|
|
30
|
-
const { channelConfig, openingData } = await openChannel({
|
|
37
|
+
const { channelConfig, openingData, meta } = await openChannel({
|
|
31
38
|
channelId,
|
|
32
39
|
createSessionServices,
|
|
33
40
|
request,
|
|
@@ -52,7 +59,9 @@ export const runChannelConnect = async ({ singletonServices, channelId, channelO
|
|
|
52
59
|
if (createSessionServices) {
|
|
53
60
|
sessionServices = await createSessionServices(singletonServices, { http }, await userSession.get());
|
|
54
61
|
}
|
|
55
|
-
|
|
62
|
+
if (channelConfig.onConnect && meta.connectPikkuFuncName) {
|
|
63
|
+
await runPikkuFuncDirectly(meta.connectPikkuFuncName, { ...singletonServices, ...sessionServices, channel }, openingData);
|
|
64
|
+
}
|
|
56
65
|
http?.response?.status(101);
|
|
57
66
|
}
|
|
58
67
|
catch (e) {
|
|
@@ -72,7 +81,7 @@ export const runChannelConnect = async ({ singletonServices, channelId, channelO
|
|
|
72
81
|
export const runChannelDisconnect = async ({ singletonServices, ...params }) => {
|
|
73
82
|
let sessionServices;
|
|
74
83
|
const { openingData, channelName, session } = await params.channelStore.getChannelAndSession(params.channelId);
|
|
75
|
-
const { channel, channelConfig } = getVariablesForChannel({
|
|
84
|
+
const { channel, channelConfig, meta } = getVariablesForChannel({
|
|
76
85
|
...params,
|
|
77
86
|
openingData,
|
|
78
87
|
channelName,
|
|
@@ -80,7 +89,9 @@ export const runChannelDisconnect = async ({ singletonServices, ...params }) =>
|
|
|
80
89
|
if (!sessionServices && params.createSessionServices) {
|
|
81
90
|
sessionServices = await params.createSessionServices(singletonServices, {}, session);
|
|
82
91
|
}
|
|
83
|
-
|
|
92
|
+
if (channelConfig.onDisconnect && meta.disconnectPikkuFuncName) {
|
|
93
|
+
await runPikkuFuncDirectly(meta.disconnectPikkuFuncName, { ...singletonServices, ...sessionServices, channel }, undefined);
|
|
94
|
+
}
|
|
84
95
|
await params.channelStore.removeChannels([channel.channelId]);
|
|
85
96
|
if (sessionServices) {
|
|
86
97
|
await closeSessionServices(singletonServices.logger, sessionServices);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { CoreServices, CoreSingletonServices, CoreUserSession } from '../types/core.types.js';
|
|
2
|
+
import { CoreAPIFunction, CoreAPIFunctionSessionless, CorePermissionGroup } from './functions.types.js';
|
|
3
|
+
export declare const addFunction: (funcName: string, func: CoreAPIFunction<any, any> | CoreAPIFunctionSessionless<any, any>) => void;
|
|
4
|
+
export declare const runPikkuFuncDirectly: <In, Out>(funcName: string, allServices: CoreServices, data: In, session?: CoreUserSession) => Promise<Out>;
|
|
5
|
+
export declare const runPikkuFunc: <In = any, Out = any>(funcName: string, { singletonServices, getAllServices, data, session, permissions, coerceDataFromSchema, }: {
|
|
6
|
+
singletonServices: CoreSingletonServices;
|
|
7
|
+
getAllServices: () => Promise<CoreServices> | CoreServices;
|
|
8
|
+
data: In;
|
|
9
|
+
session?: CoreUserSession;
|
|
10
|
+
permissions?: CorePermissionGroup;
|
|
11
|
+
coerceDataFromSchema?: boolean;
|
|
12
|
+
}) => Promise<Out>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ForbiddenError } from '../errors/errors.js';
|
|
2
|
+
import { verifyPermissions } from '../permissions.js';
|
|
3
|
+
import { pikkuState } from '../pikku-state.js';
|
|
4
|
+
import { coerceTopLevelDataFromSchema, validateSchema } from '../schema.js';
|
|
5
|
+
export const addFunction = (funcName, func) => {
|
|
6
|
+
pikkuState('functions', 'nameToFunction').set(funcName, func);
|
|
7
|
+
};
|
|
8
|
+
export const runPikkuFuncDirectly = async (funcName, allServices, data, session) => {
|
|
9
|
+
const func = pikkuState('functions', 'nameToFunction').get(funcName);
|
|
10
|
+
if (!func) {
|
|
11
|
+
throw new Error(`Function not found: ${funcName}`);
|
|
12
|
+
}
|
|
13
|
+
return (await func(allServices, data, session));
|
|
14
|
+
};
|
|
15
|
+
export const runPikkuFunc = async (funcName, { singletonServices, getAllServices, data, session, permissions, coerceDataFromSchema, }) => {
|
|
16
|
+
const func = pikkuState('functions', 'nameToFunction').get(funcName);
|
|
17
|
+
if (!func) {
|
|
18
|
+
throw new Error(`Function not found: ${funcName}`);
|
|
19
|
+
}
|
|
20
|
+
const funcMeta = pikkuState('functions', 'meta')[funcName];
|
|
21
|
+
if (!funcMeta) {
|
|
22
|
+
throw new Error(`Function meta not found: ${funcName}`);
|
|
23
|
+
}
|
|
24
|
+
const schemaName = funcMeta.schemaName;
|
|
25
|
+
// Validate request data against the defined schema, if any
|
|
26
|
+
await validateSchema(singletonServices.logger, singletonServices.schema, schemaName, data);
|
|
27
|
+
// Coerce (top level) query string parameters or date objects if specified by the schema
|
|
28
|
+
if (coerceDataFromSchema && schemaName) {
|
|
29
|
+
coerceTopLevelDataFromSchema(schemaName, data);
|
|
30
|
+
}
|
|
31
|
+
const allServices = await getAllServices();
|
|
32
|
+
// Execute permission checks
|
|
33
|
+
const permissioned = await verifyPermissions(permissions, allServices, data, session);
|
|
34
|
+
if (permissioned === false) {
|
|
35
|
+
throw new ForbiddenError('Permission denied');
|
|
36
|
+
}
|
|
37
|
+
return (await func(allServices, data, session));
|
|
38
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { PikkuChannel } from '../channel/channel.types.js';
|
|
2
|
+
import type { CoreServices, CoreSingletonServices, CoreUserSession } from '../types/core.types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Represents a core API function that performs an operation using core services and a user session.
|
|
5
|
+
*
|
|
6
|
+
* @template In - The input type.
|
|
7
|
+
* @template Out - The output type.
|
|
8
|
+
* @template Services - The services type, defaults to `CoreServices`.
|
|
9
|
+
* @template Session - The session type, defaults to `CoreUserSession`.
|
|
10
|
+
*/
|
|
11
|
+
export type CoreAPIFunction<In, Out, ChannelData extends unknown | null = null, Services extends CoreSingletonServices = CoreServices & (ChannelData extends null ? {
|
|
12
|
+
channel?: PikkuChannel<unknown, Out> | undefined;
|
|
13
|
+
} : {
|
|
14
|
+
channel: PikkuChannel<ChannelData, Out>;
|
|
15
|
+
}), Session extends CoreUserSession = CoreUserSession> = (services: Services, data: In, session: Session) => ChannelData extends null ? Promise<Out> : Promise<Out> | Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Represents a core API function that can be used without a session.
|
|
18
|
+
*
|
|
19
|
+
* @template In - The input type.
|
|
20
|
+
* @template Out - The output type.
|
|
21
|
+
* @template Services - The services type, defaults to `CoreServices`.
|
|
22
|
+
* @template Session - The session type, defaults to `CoreUserSession`.
|
|
23
|
+
*/
|
|
24
|
+
export type CoreAPIFunctionSessionless<In, Out, ChannelData extends unknown | null = null, Services extends CoreSingletonServices = CoreServices & (ChannelData extends null ? {
|
|
25
|
+
channel?: PikkuChannel<unknown, Out> | undefined;
|
|
26
|
+
} : {
|
|
27
|
+
channel: PikkuChannel<ChannelData, Out>;
|
|
28
|
+
}), Session extends CoreUserSession = CoreUserSession> = (services: Services, data: In, session?: Session) => ChannelData extends null ? Promise<Out> : Promise<Out> | Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Represents a function that checks permissions for a given API operation.
|
|
31
|
+
*
|
|
32
|
+
* @template In - The input type.
|
|
33
|
+
* @template Services - The services type, defaults to `CoreServices`.
|
|
34
|
+
* @template Session - The session type, defaults to `CoreUserSession`.
|
|
35
|
+
*/
|
|
36
|
+
export type CoreAPIPermission<In = any, Services extends CoreSingletonServices = CoreServices, Session extends CoreUserSession = CoreUserSession> = (services: Services, data: In, session?: Session) => Promise<boolean>;
|
|
37
|
+
export type CorePermissionGroup<APIPermission = CoreAPIPermission<any>> = Record<string, APIPermission | APIPermission[]> | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/handle-error.d.ts
CHANGED
|
@@ -24,11 +24,11 @@ export declare const addMiddleware: <APIMiddleware extends PikkuMiddleware>(rout
|
|
|
24
24
|
* @template Route Route pattern as a string.
|
|
25
25
|
* @template APIFunction Type for the route handler function.
|
|
26
26
|
* @template APIFunctionSessionless Type for a sessionless handler.
|
|
27
|
-
* @template
|
|
27
|
+
* @template APIPermissionGroup Type representing required permissions.
|
|
28
28
|
* @template APIMiddleware Middleware type to be used with the route.
|
|
29
29
|
* @param {CoreHTTPFunctionRoute<In, Out, Route, APIFunction, APIFunctionSessionless, APIPermission, APIMiddleware>} route - The route configuration object.
|
|
30
30
|
*/
|
|
31
|
-
export declare const addRoute: <In, Out, Route extends string, APIFunction, APIFunctionSessionless,
|
|
31
|
+
export declare const addRoute: <In, Out, Route extends string, APIFunction, APIFunctionSessionless, APIPermissionGroup, APIMiddleware>(route: CoreHTTPFunctionRoute<In, Out, Route, APIFunction, APIFunctionSessionless, APIPermissionGroup, APIMiddleware>) => void;
|
|
32
32
|
/**
|
|
33
33
|
* Combines the request and response objects into a single HTTP interaction object.
|
|
34
34
|
*
|
|
@@ -87,4 +87,4 @@ export declare const pikkuFetch: <In, Out>(request: Request | PikkuHTTPRequest,
|
|
|
87
87
|
* @param {RunRouteOptions & RunRouteParams} options - Options such as singleton services, session handling, and error configuration.
|
|
88
88
|
* @returns {Promise<Out | void>} The output from the route handler or void if an error occurred.
|
|
89
89
|
*/
|
|
90
|
-
export declare const fetchData: <In, Out>(request: Request | PikkuHTTPRequest, response: PikkuHTTPResponse, { singletonServices, createSessionServices, skipUserSession, respondWith404, logWarningsForStatusCodes, coerceDataFromSchema, bubbleErrors, }: RunRouteOptions & RunRouteParams) => Promise<Out | void>;
|
|
90
|
+
export declare const fetchData: <In, Out>(request: Request | PikkuHTTPRequest, response: PikkuHTTPResponse, { singletonServices, createSessionServices, skipUserSession, respondWith404, logWarningsForStatusCodes, coerceDataFromSchema, bubbleErrors, generateRequestId, }: RunRouteOptions & RunRouteParams) => Promise<Out | void>;
|