@pikku/core 0.6.13 → 0.6.15
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 +13 -0
- package/dist/channel/channel-handler.d.ts +2 -2
- package/dist/channel/channel-handler.js +4 -4
- package/dist/channel/channel-runner.d.ts +5 -4
- package/dist/channel/channel-runner.js +8 -14
- package/dist/channel/channel.types.d.ts +11 -18
- package/dist/channel/local/local-channel-handler.d.ts +1 -3
- package/dist/channel/local/local-channel-handler.js +0 -3
- package/dist/channel/local/local-channel-runner.js +49 -33
- package/dist/channel/pikku-abstract-channel-handler.d.ts +4 -7
- package/dist/channel/pikku-abstract-channel-handler.js +1 -5
- package/dist/channel/serverless/serverless-channel-runner.js +45 -38
- package/dist/handle-error.d.ts +14 -0
- package/dist/handle-error.js +53 -0
- package/dist/http/http-route-runner.d.ts +34 -11
- package/dist/http/http-route-runner.js +177 -106
- package/dist/http/http-routes.types.d.ts +15 -9
- package/dist/http/index.d.ts +0 -1
- package/dist/http/index.js +0 -1
- package/dist/http/pikku-http-abstract-request.d.ts +4 -1
- package/dist/http/pikku-http-abstract-request.js +7 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/middleware/auth-api-key.d.ts +9 -0
- package/dist/middleware/auth-api-key.js +23 -0
- package/dist/middleware/auth-cookie-middleware.d.ts +11 -0
- package/dist/middleware/auth-cookie-middleware.js +36 -0
- package/dist/middleware/auth-jwt-middleware.d.ts +10 -0
- package/dist/middleware/auth-jwt-middleware.js +32 -0
- package/dist/middleware/auth-query-middleware.d.ts +10 -0
- package/dist/middleware/auth-query-middleware.js +21 -0
- package/dist/middleware/index.d.ts +4 -0
- package/dist/middleware/index.js +4 -0
- package/dist/middleware-runner.d.ts +22 -0
- package/dist/middleware-runner.js +28 -0
- package/dist/scheduler/scheduler.types.d.ts +2 -0
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +3 -6
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/user-session-service.d.ts +20 -3
- package/dist/services/user-session-service.js +35 -1
- package/dist/types/core.types.d.ts +11 -15
- package/dist/types/functions.types.d.ts +4 -4
- package/lcov.info +1572 -1729
- package/package.json +1 -2
- package/src/channel/channel-handler.ts +6 -11
- package/src/channel/channel-runner.ts +13 -32
- package/src/channel/channel.types.ts +13 -39
- package/src/channel/local/local-channel-handler.ts +1 -7
- package/src/channel/local/local-channel-runner.test.ts +2 -5
- package/src/channel/local/local-channel-runner.ts +72 -54
- package/src/channel/pikku-abstract-channel-handler.test.ts +3 -28
- package/src/channel/pikku-abstract-channel-handler.ts +3 -9
- package/src/channel/serverless/serverless-channel-runner.ts +68 -56
- package/src/handle-error.ts +67 -0
- package/src/http/http-route-runner.test.ts +12 -45
- package/src/http/http-route-runner.ts +256 -182
- package/src/http/http-routes.types.ts +15 -10
- package/src/http/index.ts +0 -2
- package/src/http/pikku-http-abstract-request.ts +8 -1
- package/src/index.ts +2 -0
- package/src/middleware/auth-api-key.ts +32 -0
- package/src/middleware/auth-cookie-middleware.ts +54 -0
- package/src/middleware/auth-jwt-middleware.ts +47 -0
- package/src/middleware/auth-query-middleware.ts +33 -0
- package/src/middleware/index.ts +4 -0
- package/src/middleware-runner.ts +43 -0
- package/src/scheduler/scheduler.types.ts +2 -0
- package/src/schema.ts +4 -8
- package/src/services/index.ts +1 -0
- package/src/services/user-session-service.ts +45 -3
- package/src/types/core.types.ts +19 -18
- package/src/types/functions.types.ts +11 -7
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/http/http-session-service.d.ts +0 -15
- package/dist/http/http-session-service.js +0 -1
- package/dist/http/pikku-http-session-service.d.ts +0 -45
- package/dist/http/pikku-http-session-service.js +0 -92
- package/src/http/http-session-service.ts +0 -19
- package/src/http/pikku-http-session-service.test.ts +0 -106
- package/src/http/pikku-http-session-service.ts +0 -135
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
## 0.6
|
|
2
2
|
|
|
3
|
+
## 0.6.15
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 1c7dfb6: fix: fixing some import issues
|
|
8
|
+
|
|
9
|
+
## 0.6.14
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- c1d8381: feat: adding filtering by tags to minimize produced payload
|
|
14
|
+
- ee5c874: feat: moving towards using middleware for http and channels
|
|
15
|
+
|
|
3
16
|
## 0.6.13
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { CoreServices
|
|
1
|
+
import { CoreServices } from '../types/core.types.js';
|
|
2
2
|
import { CoreAPIChannel, PikkuChannelHandler } from './channel.types.js';
|
|
3
|
-
export declare const processMessageHandlers: (services: CoreServices, channelConfig: CoreAPIChannel<any, any>, channelHandler: PikkuChannelHandler
|
|
3
|
+
export declare const processMessageHandlers: (services: CoreServices, channelConfig: CoreAPIChannel<any, any>, channelHandler: PikkuChannelHandler) => (rawData: any) => Promise<unknown>;
|
|
@@ -17,20 +17,20 @@ const validateSchema = (logger, data, channelName, routingProperty, routerValue)
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
|
-
const validateAuth = (requiresSession,
|
|
20
|
+
const validateAuth = (requiresSession, userSession, onMessage) => {
|
|
21
21
|
const auth = typeof onMessage === 'function'
|
|
22
22
|
? requiresSession
|
|
23
23
|
: onMessage.auth === undefined
|
|
24
24
|
? requiresSession
|
|
25
25
|
: onMessage.auth;
|
|
26
|
-
if (auth && !
|
|
26
|
+
if (auth && !userSession) {
|
|
27
27
|
return false;
|
|
28
28
|
}
|
|
29
29
|
return true;
|
|
30
30
|
};
|
|
31
|
-
const validatePermissions = async (services,
|
|
31
|
+
const validatePermissions = async (services, userSession, onMessage, data) => {
|
|
32
32
|
const permissions = typeof onMessage === 'function' ? {} : onMessage.permissions;
|
|
33
|
-
return await verifyPermissions(permissions, services, data,
|
|
33
|
+
return await verifyPermissions(permissions, services, data, userSession);
|
|
34
34
|
};
|
|
35
35
|
const runFunction = async (services, channelHandler, onMessage, data) => {
|
|
36
36
|
const func = typeof onMessage === 'function' ? onMessage : onMessage.func;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { UserSessionService } from '../services/user-session-service.js';
|
|
2
2
|
import { CoreAPIChannel, ChannelsMeta, CoreAPIChannels, RunChannelOptions, RunChannelParams } from './channel.types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Returns all the registered routes and associated metadata.
|
|
@@ -16,11 +16,12 @@ export declare const setChannelsMeta: (_channelsMeta: ChannelsMeta) => void;
|
|
|
16
16
|
export declare const getMatchingChannelConfig: (request: string) => {
|
|
17
17
|
matchedPath: import("path-to-regexp").MatchResult<Partial<Record<string, string | string[]>>>;
|
|
18
18
|
params: Partial<Record<string, string | string[]>>;
|
|
19
|
-
channelConfig: CoreAPIChannel<any, string, import("./channel.types.js").CoreChannelConnection<any, unknown, import("../
|
|
19
|
+
channelConfig: CoreAPIChannel<any, string, import("./channel.types.js").CoreChannelConnection<any, unknown, import("../index.js").CoreServices>, import("./channel.types.js").CoreChannelDisconnection<any, import("../index.js").CoreServices>, import("./channel.types.js").CoreChannelMessage<unknown, unknown, unknown, import("../index.js").CoreServices>, import("./channel.types.js").CoreChannelMessage<unknown, unknown, unknown, import("../index.js").CoreServices>, import("../index.js").CoreAPIPermission<any>>;
|
|
20
20
|
schemaName: string | null | undefined;
|
|
21
21
|
} | null;
|
|
22
|
-
export declare const openChannel: ({ route, singletonServices,
|
|
23
|
-
|
|
22
|
+
export declare const openChannel: ({ route, singletonServices, coerceToArray, http, userSessionService, }: Pick<CoreAPIChannel<unknown, string>, "route"> & Omit<RunChannelParams<unknown>, "response" | "request"> & {
|
|
23
|
+
userSessionService?: UserSessionService<any>;
|
|
24
|
+
} & RunChannelOptions) => Promise<{
|
|
24
25
|
openingData: unknown;
|
|
25
26
|
channelConfig: CoreAPIChannel<unknown, any>;
|
|
26
27
|
}>;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { NotFoundError } from '../errors/errors.js';
|
|
2
|
-
import {
|
|
3
|
-
import { validateAndCoerce } from '../schema.js';
|
|
2
|
+
import { coerceQueryStringToArray, validateSchema } from '../schema.js';
|
|
4
3
|
import { match } from 'path-to-regexp';
|
|
5
4
|
if (!globalThis.pikku?.channels) {
|
|
6
5
|
globalThis.pikku = globalThis.pikku || {};
|
|
@@ -59,28 +58,23 @@ export const getMatchingChannelConfig = (request) => {
|
|
|
59
58
|
}
|
|
60
59
|
return null;
|
|
61
60
|
};
|
|
62
|
-
export const openChannel = async ({ route, singletonServices,
|
|
61
|
+
export const openChannel = async ({ route, singletonServices, coerceToArray = false, http, userSessionService, }) => {
|
|
63
62
|
const matchingChannel = getMatchingChannelConfig(route);
|
|
64
63
|
if (!matchingChannel) {
|
|
65
64
|
singletonServices.logger.info(`Channel not found: ${route}`);
|
|
66
65
|
throw new NotFoundError(`Channel not found: ${route}`);
|
|
67
66
|
}
|
|
68
|
-
const {
|
|
67
|
+
const { params, channelConfig, schemaName } = matchingChannel;
|
|
69
68
|
const requiresSession = channelConfig.auth !== false;
|
|
70
69
|
http?.request?.setParams(params);
|
|
71
70
|
singletonServices.logger.info(`Matched channel: ${channelConfig.name} | route: ${channelConfig.route} | auth: ${requiresSession.toString()}`);
|
|
72
|
-
const userSession = await loadUserSession(skipUserSession,
|
|
73
|
-
// We may require a session, but we don't actually need it
|
|
74
|
-
// on connect since channels can authenticate later given
|
|
75
|
-
// how websocket sessions work (cookie or queryParam based)
|
|
76
|
-
false, http, matchedPath, channelConfig, singletonServices.logger, singletonServices.httpSessionService);
|
|
77
|
-
if (singletonServices.enforceChannelAccess) {
|
|
78
|
-
await singletonServices.enforceChannelAccess(matchingChannel.channelConfig, userSession);
|
|
79
|
-
}
|
|
80
71
|
let openingData;
|
|
81
72
|
if (http?.request) {
|
|
82
73
|
openingData = await http.request.getData();
|
|
83
|
-
|
|
74
|
+
if (coerceToArray && schemaName) {
|
|
75
|
+
coerceQueryStringToArray(schemaName, openingData);
|
|
76
|
+
}
|
|
77
|
+
validateSchema(singletonServices.logger, singletonServices.schemaService, schemaName, openingData);
|
|
84
78
|
}
|
|
85
|
-
return {
|
|
79
|
+
return { openingData, channelConfig };
|
|
86
80
|
};
|
|
@@ -2,7 +2,7 @@ import { PikkuError } from '../errors/error-handler.js';
|
|
|
2
2
|
import { HTTPFunctionMetaInputTypes, PikkuHTTP } from '../http/http-routes.types.js';
|
|
3
3
|
import { PikkuHTTPAbstractRequest } from '../http/pikku-http-abstract-request.js';
|
|
4
4
|
import { PikkuHTTPAbstractResponse } from '../http/pikku-http-abstract-response.js';
|
|
5
|
-
import { APIDocs, CoreServices, CoreSingletonServices,
|
|
5
|
+
import { APIDocs, CoreServices, CoreSingletonServices, CreateSessionServices, PikkuMiddleware } from '../types/core.types.js';
|
|
6
6
|
import { CoreAPIPermission } from '../types/functions.types.js';
|
|
7
7
|
import { PikkuRequest } from '../pikku-request.js';
|
|
8
8
|
import { PikkuResponse } from '../pikku-response.js';
|
|
@@ -41,10 +41,11 @@ export interface ChannelMeta {
|
|
|
41
41
|
outputs: string[] | null;
|
|
42
42
|
}>>;
|
|
43
43
|
docs?: APIDocs;
|
|
44
|
+
tags?: string[];
|
|
44
45
|
}
|
|
45
46
|
export type ChannelsMeta = ChannelMeta[];
|
|
46
|
-
export type CoreChannelConnection<ChannelData, Out = unknown, Services extends CoreServices = CoreServices
|
|
47
|
-
export type CoreChannelDisconnection<ChannelData, Services extends CoreServices = CoreServices
|
|
47
|
+
export type CoreChannelConnection<ChannelData, Out = unknown, Services extends CoreServices = CoreServices> = (services: Services, channel: PikkuChannel<ChannelData, Out>) => Promise<void>;
|
|
48
|
+
export type CoreChannelDisconnection<ChannelData, Services extends CoreServices = CoreServices> = (services: Services, channel: PikkuChannel<ChannelData, never>) => Promise<void>;
|
|
48
49
|
/**
|
|
49
50
|
* Represents a core channel function that performs an operation using core services and a user session.
|
|
50
51
|
*
|
|
@@ -52,7 +53,7 @@ export type CoreChannelDisconnection<ChannelData, Services extends CoreServices
|
|
|
52
53
|
* @template Services - The services type, defaults to `CoreServices`.
|
|
53
54
|
* @template Session - The session type, defaults to `CoreUserSession`.
|
|
54
55
|
*/
|
|
55
|
-
export type CoreChannelMessage<In, Out, ChannelData, Services extends CoreServices = CoreServices
|
|
56
|
+
export type CoreChannelMessage<In, Out, ChannelData, Services extends CoreServices = CoreServices> = (services: Services, channel: PikkuChannel<ChannelData, Out>, data: In) => Promise<void | Out>;
|
|
56
57
|
export type CoreAPIChannelMessage<ChannelFunctionMessage = CoreChannelMessage<unknown, unknown, unknown>> = {
|
|
57
58
|
func: ChannelFunctionMessage;
|
|
58
59
|
route: string;
|
|
@@ -72,6 +73,7 @@ export type CoreAPIChannel<ChannelData, Channel extends string, ChannelFunctionC
|
|
|
72
73
|
permissions?: Record<string, APIPermission[] | APIPermission>;
|
|
73
74
|
auth?: boolean;
|
|
74
75
|
}>>;
|
|
76
|
+
middleware?: PikkuMiddleware[];
|
|
75
77
|
permissions?: Record<string, APIPermission[] | APIPermission>;
|
|
76
78
|
auth?: boolean;
|
|
77
79
|
docs?: Partial<{
|
|
@@ -80,27 +82,18 @@ export type CoreAPIChannel<ChannelData, Channel extends string, ChannelFunctionC
|
|
|
80
82
|
errors: Array<typeof PikkuError>;
|
|
81
83
|
tags: string[];
|
|
82
84
|
}>;
|
|
85
|
+
tags?: string[];
|
|
83
86
|
};
|
|
84
87
|
export type CoreAPIChannels = CoreAPIChannel<any, string>[];
|
|
85
|
-
export interface PikkuChannel<
|
|
88
|
+
export interface PikkuChannel<OpeningData, Out> {
|
|
86
89
|
channelId: string;
|
|
87
|
-
userSession?: UserSession;
|
|
88
|
-
setUserSession: (userSession: UserSession) => Promise<void> | void;
|
|
89
90
|
openingData: OpeningData;
|
|
90
91
|
send: (data: Out, isBinary?: boolean) => Promise<void> | void;
|
|
91
92
|
close: () => Promise<void> | void;
|
|
92
93
|
state: 'initial' | 'open' | 'closed';
|
|
93
94
|
}
|
|
94
|
-
export interface PikkuChannelHandler<
|
|
95
|
-
setUserSession(session: UserSession): Promise<void> | void;
|
|
95
|
+
export interface PikkuChannelHandler<OpeningData = unknown, Out = unknown> {
|
|
96
96
|
send(message: Out, isBinary?: boolean): Promise<void> | void;
|
|
97
|
-
getChannel(): PikkuChannel<
|
|
97
|
+
getChannel(): PikkuChannel<OpeningData, Out>;
|
|
98
98
|
}
|
|
99
|
-
export type PikkuChannelHandlerFactory<OpeningData = unknown,
|
|
100
|
-
/**
|
|
101
|
-
* Enfore access to a channel.
|
|
102
|
-
* @param route - The channel to verify access for.
|
|
103
|
-
* @param session - The user session.
|
|
104
|
-
* @returns A promise that resolves if access is granted.
|
|
105
|
-
*/
|
|
106
|
-
export type enforceChannelAccess = (channel: CoreAPIChannel<unknown, any>, session?: CoreUserSession) => Promise<void> | void;
|
|
99
|
+
export type PikkuChannelHandlerFactory<OpeningData = unknown, Out = unknown> = (channelId: string, channelName: string, openingData: OpeningData) => PikkuChannelHandler<OpeningData, Out>;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { CoreUserSession } from '../../types/core.types.js';
|
|
2
1
|
import { PikkuAbstractChannelHandler } from '../pikku-abstract-channel-handler.js';
|
|
3
|
-
export declare class PikkuLocalChannelHandler<
|
|
2
|
+
export declare class PikkuLocalChannelHandler<OpeningData = unknown, Out = unknown> extends PikkuAbstractChannelHandler<OpeningData, Out> {
|
|
4
3
|
private onMessageCallback?;
|
|
5
4
|
private openCallBack?;
|
|
6
5
|
private closeCallback?;
|
|
@@ -13,5 +12,4 @@ export declare class PikkuLocalChannelHandler<UserSession extends CoreUserSessio
|
|
|
13
12
|
close(): void;
|
|
14
13
|
registerOnSend(send: (message: Out) => void): void;
|
|
15
14
|
send(message: Out, isBinary?: boolean): void;
|
|
16
|
-
setUserSession(userSession: UserSession): void;
|
|
17
15
|
}
|
|
@@ -1,46 +1,62 @@
|
|
|
1
1
|
import { openChannel } from '../channel-runner.js';
|
|
2
|
-
import { createHTTPInteraction
|
|
2
|
+
import { createHTTPInteraction } from '../../http/http-route-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
|
+
import { handleError } from '../../handle-error.js';
|
|
7
|
+
import { runMiddleware } from '../../middleware-runner.js';
|
|
8
|
+
import { LocalUserSessionService } from '../../services/user-session-service.js';
|
|
6
9
|
export const runLocalChannel = async ({ singletonServices, channelId, request, response, route, createSessionServices, skipUserSession = false, respondWith404 = true, coerceToArray = false, logWarningsForStatusCodes = [], bubbleErrors = false, }) => {
|
|
7
10
|
let sessionServices;
|
|
11
|
+
let channelHandler;
|
|
12
|
+
const userSessionService = new LocalUserSessionService();
|
|
8
13
|
const http = createHTTPInteraction(request, response);
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
const main = async () => {
|
|
15
|
+
try {
|
|
16
|
+
const { openingData, channelConfig } = await openChannel({
|
|
17
|
+
channelId,
|
|
18
|
+
createSessionServices,
|
|
19
|
+
respondWith404,
|
|
20
|
+
http,
|
|
21
|
+
route,
|
|
22
|
+
singletonServices,
|
|
23
|
+
skipUserSession,
|
|
24
|
+
coerceToArray,
|
|
25
|
+
});
|
|
26
|
+
channelHandler = new PikkuLocalChannelHandler(channelId, channelConfig.name, openingData);
|
|
27
|
+
const channel = channelHandler.getChannel();
|
|
28
|
+
const userSession = await userSessionService.get();
|
|
29
|
+
if (createSessionServices) {
|
|
30
|
+
sessionServices = await createSessionServices(singletonServices, { http }, userSession);
|
|
31
|
+
}
|
|
32
|
+
const allServices = {
|
|
33
|
+
...singletonServices,
|
|
34
|
+
...sessionServices,
|
|
35
|
+
userSessionService,
|
|
36
|
+
};
|
|
37
|
+
channelHandler.registerOnOpen(() => {
|
|
38
|
+
channelConfig.onConnect?.(allServices, channel);
|
|
39
|
+
});
|
|
40
|
+
channelHandler.registerOnClose(async () => {
|
|
41
|
+
channelConfig.onDisconnect?.(allServices, channel);
|
|
42
|
+
if (sessionServices) {
|
|
43
|
+
await closeSessionServices(singletonServices.logger, sessionServices);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
channelHandler.registerOnMessage(processMessageHandlers(allServices, channelConfig, channelHandler));
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
handleError(e, http, channelId, singletonServices.logger, logWarningsForStatusCodes, respondWith404, bubbleErrors);
|
|
24
50
|
}
|
|
25
|
-
|
|
26
|
-
channelHandler.registerOnOpen(() => {
|
|
27
|
-
channelConfig.onConnect?.(allServices, channel);
|
|
28
|
-
});
|
|
29
|
-
channelHandler.registerOnClose(async () => {
|
|
30
|
-
channelConfig.onDisconnect?.(allServices, channel);
|
|
51
|
+
finally {
|
|
31
52
|
if (sessionServices) {
|
|
32
53
|
await closeSessionServices(singletonServices.logger, sessionServices);
|
|
33
54
|
}
|
|
34
|
-
});
|
|
35
|
-
channelHandler.registerOnMessage(processMessageHandlers(allServices, channelConfig, channelHandler));
|
|
36
|
-
return channelHandler;
|
|
37
|
-
}
|
|
38
|
-
catch (e) {
|
|
39
|
-
handleError(e, http, channelId, singletonServices.logger, logWarningsForStatusCodes, respondWith404, bubbleErrors);
|
|
40
|
-
}
|
|
41
|
-
finally {
|
|
42
|
-
if (sessionServices) {
|
|
43
|
-
await closeSessionServices(singletonServices.logger, sessionServices);
|
|
44
55
|
}
|
|
45
|
-
}
|
|
56
|
+
};
|
|
57
|
+
await runMiddleware({
|
|
58
|
+
...singletonServices,
|
|
59
|
+
userSessionService,
|
|
60
|
+
}, { http }, route.middleware || [], main);
|
|
61
|
+
return channelHandler;
|
|
46
62
|
};
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
import { CoreUserSession } from '../types/core.types.js';
|
|
2
1
|
import { PikkuChannel, PikkuChannelHandler } from './channel.types.js';
|
|
3
|
-
export declare abstract class PikkuAbstractChannelHandler<
|
|
2
|
+
export declare abstract class PikkuAbstractChannelHandler<OpeningData = unknown, Out = unknown> implements PikkuChannelHandler<OpeningData, Out> {
|
|
4
3
|
channelId: string;
|
|
5
4
|
channelName: string;
|
|
6
|
-
protected userSession: UserSession | undefined;
|
|
7
5
|
protected openingData: OpeningData;
|
|
8
|
-
protected channel?: PikkuChannel<
|
|
9
|
-
constructor(channelId: string, channelName: string,
|
|
10
|
-
abstract setUserSession(userSession: UserSession): Promise<void> | void;
|
|
6
|
+
protected channel?: PikkuChannel<OpeningData, Out>;
|
|
7
|
+
constructor(channelId: string, channelName: string, openingData: OpeningData);
|
|
11
8
|
abstract send(message: Out, isBinary?: boolean): Promise<void> | void;
|
|
12
|
-
getChannel(): PikkuChannel<
|
|
9
|
+
getChannel(): PikkuChannel<OpeningData, Out>;
|
|
13
10
|
open(): void;
|
|
14
11
|
close(): Promise<void> | void;
|
|
15
12
|
}
|
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
export class PikkuAbstractChannelHandler {
|
|
2
2
|
channelId;
|
|
3
3
|
channelName;
|
|
4
|
-
userSession;
|
|
5
4
|
openingData;
|
|
6
5
|
channel;
|
|
7
|
-
constructor(channelId, channelName,
|
|
6
|
+
constructor(channelId, channelName, openingData) {
|
|
8
7
|
this.channelId = channelId;
|
|
9
8
|
this.channelName = channelName;
|
|
10
|
-
this.userSession = userSession;
|
|
11
9
|
this.openingData = openingData;
|
|
12
10
|
}
|
|
13
11
|
getChannel() {
|
|
14
12
|
if (!this.channel) {
|
|
15
13
|
this.channel = {
|
|
16
14
|
channelId: this.channelId,
|
|
17
|
-
userSession: this.userSession,
|
|
18
15
|
openingData: this.openingData,
|
|
19
|
-
setUserSession: this.setUserSession.bind(this),
|
|
20
16
|
send: this.send.bind(this),
|
|
21
17
|
close: this.close.bind(this),
|
|
22
18
|
state: 'initial',
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { closeSessionServices } from '../../utils.js';
|
|
2
2
|
import { processMessageHandlers } from '../channel-handler.js';
|
|
3
3
|
import { getChannels, openChannel } from '../channel-runner.js';
|
|
4
|
-
import { createHTTPInteraction
|
|
5
|
-
|
|
4
|
+
import { createHTTPInteraction } from '../../http/http-route-runner.js';
|
|
5
|
+
import { handleError } from '../../handle-error.js';
|
|
6
|
+
import { RemoteUserSessionService } from '../../services/user-session-service.js';
|
|
7
|
+
import { runMiddleware } from '../../middleware-runner.js';
|
|
8
|
+
const getVariablesForChannel = ({ channelId, channelName, channelHandlerFactory, openingData, }) => {
|
|
6
9
|
const { channels } = getChannels();
|
|
7
10
|
const channelConfig = channels.find((channelConfig) => channelConfig.name === channelName);
|
|
8
11
|
if (!channelConfig) {
|
|
9
12
|
throw new Error(`Channel not found: ${channelName}`);
|
|
10
13
|
}
|
|
11
|
-
const channelHandler = channelHandlerFactory(channelId, channelConfig.name, openingData
|
|
14
|
+
const channelHandler = channelHandlerFactory(channelId, channelConfig.name, openingData);
|
|
12
15
|
return {
|
|
13
16
|
channelConfig,
|
|
14
17
|
channelHandler,
|
|
@@ -18,48 +21,53 @@ const getVariablesForChannel = ({ channelId, userSession, channelName, channelHa
|
|
|
18
21
|
export const runChannelConnect = async ({ singletonServices, channelId, channelObject, request, response, route, createSessionServices, channelStore, channelHandlerFactory, coerceToArray = false, logWarningsForStatusCodes = [], respondWith404 = true, bubbleErrors = false, }) => {
|
|
19
22
|
let sessionServices;
|
|
20
23
|
const http = createHTTPInteraction(request, response);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
24
|
+
const userSessionService = new RemoteUserSessionService(channelStore, channelId);
|
|
25
|
+
const { channelConfig, openingData } = await openChannel({
|
|
26
|
+
channelId,
|
|
27
|
+
createSessionServices,
|
|
28
|
+
http,
|
|
29
|
+
route,
|
|
30
|
+
singletonServices,
|
|
31
|
+
coerceToArray,
|
|
32
|
+
});
|
|
33
|
+
const main = async () => {
|
|
34
|
+
try {
|
|
35
|
+
await channelStore.addChannel({
|
|
36
|
+
channelId,
|
|
37
|
+
channelName: channelConfig.name,
|
|
38
|
+
openingData,
|
|
39
|
+
channelObject,
|
|
40
|
+
});
|
|
41
|
+
const { channel } = getVariablesForChannel({
|
|
42
|
+
channelId,
|
|
43
|
+
channelHandlerFactory,
|
|
44
|
+
channelName: channelConfig.name,
|
|
45
|
+
});
|
|
46
|
+
if (createSessionServices) {
|
|
47
|
+
sessionServices = await createSessionServices(singletonServices, { http }, await userSessionService.get());
|
|
48
|
+
}
|
|
49
|
+
await channelConfig.onConnect?.({ ...singletonServices, ...sessionServices }, channel);
|
|
50
|
+
http?.response?.setStatus(101);
|
|
44
51
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
catch (e) {
|
|
49
|
-
handleError(e, http, channelId, singletonServices.logger, logWarningsForStatusCodes, respondWith404, bubbleErrors);
|
|
50
|
-
}
|
|
51
|
-
finally {
|
|
52
|
-
if (sessionServices) {
|
|
53
|
-
await closeSessionServices(singletonServices.logger, sessionServices);
|
|
52
|
+
catch (e) {
|
|
53
|
+
handleError(e, http, channelId, singletonServices.logger, logWarningsForStatusCodes, respondWith404, bubbleErrors);
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
finally {
|
|
56
|
+
if (sessionServices) {
|
|
57
|
+
await closeSessionServices(singletonServices.logger, sessionServices);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
await runMiddleware({
|
|
62
|
+
...singletonServices,
|
|
63
|
+
userSessionService,
|
|
64
|
+
}, { http }, channelConfig.middleware || [], main);
|
|
56
65
|
};
|
|
57
66
|
export const runChannelDisconnect = async ({ singletonServices, ...params }) => {
|
|
58
67
|
let sessionServices;
|
|
59
68
|
const { userSession, openingData, channelName } = await params.channelStore.getChannel(params.channelId);
|
|
60
69
|
const { channel, channelConfig } = getVariablesForChannel({
|
|
61
70
|
...params,
|
|
62
|
-
userSession,
|
|
63
71
|
openingData,
|
|
64
72
|
channelName,
|
|
65
73
|
});
|
|
@@ -77,7 +85,6 @@ export const runChannelMessage = async ({ singletonServices, ...params }, data)
|
|
|
77
85
|
const { userSession, openingData, channelName } = await params.channelStore.getChannel(params.channelId);
|
|
78
86
|
const { channelHandler, channelConfig } = getVariablesForChannel({
|
|
79
87
|
...params,
|
|
80
|
-
userSession,
|
|
81
88
|
openingData,
|
|
82
89
|
channelName,
|
|
83
90
|
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Logger } from './services/logger.js';
|
|
2
|
+
import { PikkuHTTP } from './http/http-routes.types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handle errors that occur during route processing
|
|
5
|
+
*
|
|
6
|
+
* @param {any} e - The error that occurred
|
|
7
|
+
* @param {PikkuHTTP | undefined} http - HTTP interaction object
|
|
8
|
+
* @param {string} trackerId - Unique ID for tracking this error
|
|
9
|
+
* @param {Logger} logger - Logger service
|
|
10
|
+
* @param {number[]} logWarningsForStatusCodes - HTTP status codes to log as warnings
|
|
11
|
+
* @param {boolean} respondWith404 - Whether to respond with 404 for NotFoundError
|
|
12
|
+
* @param {boolean} bubbleError - Whether to throw the error after handling
|
|
13
|
+
*/
|
|
14
|
+
export declare const handleError: (e: any, http: PikkuHTTP | undefined, trackerId: string, logger: Logger, logWarningsForStatusCodes: number[], respondWith404: boolean, bubbleError: boolean) => void;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { getErrorResponse } from './errors/error-handler.js';
|
|
2
|
+
import { NotFoundError } from './errors/errors.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handle errors that occur during route processing
|
|
5
|
+
*
|
|
6
|
+
* @param {any} e - The error that occurred
|
|
7
|
+
* @param {PikkuHTTP | undefined} http - HTTP interaction object
|
|
8
|
+
* @param {string} trackerId - Unique ID for tracking this error
|
|
9
|
+
* @param {Logger} logger - Logger service
|
|
10
|
+
* @param {number[]} logWarningsForStatusCodes - HTTP status codes to log as warnings
|
|
11
|
+
* @param {boolean} respondWith404 - Whether to respond with 404 for NotFoundError
|
|
12
|
+
* @param {boolean} bubbleError - Whether to throw the error after handling
|
|
13
|
+
*/
|
|
14
|
+
export const handleError = (e, http, trackerId, logger, logWarningsForStatusCodes, respondWith404, bubbleError) => {
|
|
15
|
+
// Skip 404 handling if configured to do so
|
|
16
|
+
if (e instanceof NotFoundError && !respondWith404) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
// Get appropriate error response
|
|
20
|
+
const errorResponse = getErrorResponse(e);
|
|
21
|
+
if (errorResponse != null) {
|
|
22
|
+
// Set status and response body
|
|
23
|
+
http?.response?.setStatus(errorResponse.status);
|
|
24
|
+
http?.response?.setJson({
|
|
25
|
+
message: errorResponse.message,
|
|
26
|
+
payload: e.payload,
|
|
27
|
+
traceId: trackerId,
|
|
28
|
+
});
|
|
29
|
+
// Log certain status codes as warnings
|
|
30
|
+
if (logWarningsForStatusCodes.includes(errorResponse.status)) {
|
|
31
|
+
logger.warn(`Warning id: ${trackerId}`);
|
|
32
|
+
logger.warn(e);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// Handle unexpected errors
|
|
37
|
+
logger.warn(`Error id: ${trackerId}`);
|
|
38
|
+
logger.error(e);
|
|
39
|
+
http?.response?.setStatus(500);
|
|
40
|
+
http?.response?.setJson({ errorId: trackerId });
|
|
41
|
+
}
|
|
42
|
+
// Handle 404 errors specifically
|
|
43
|
+
if (e instanceof NotFoundError) {
|
|
44
|
+
http?.response?.end();
|
|
45
|
+
}
|
|
46
|
+
// Either bubble up or end the response
|
|
47
|
+
if (bubbleError) {
|
|
48
|
+
throw e;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
http?.response?.end();
|
|
52
|
+
}
|
|
53
|
+
};
|
|
@@ -1,30 +1,53 @@
|
|
|
1
1
|
import { CoreHTTPFunctionRoute, HTTPRoutesMeta, RunRouteOptions, RunRouteParams, PikkuHTTP } from './http-routes.types.js';
|
|
2
|
-
import { CoreUserSession } from '../types/core.types.js';
|
|
3
|
-
import { PikkuHTTPAbstractRequest } from './pikku-http-abstract-request.js';
|
|
4
|
-
import { Logger } from '../services/index.js';
|
|
5
|
-
import { CoreAPIChannel } from '../channel/channel.types.js';
|
|
6
2
|
import { PikkuRequest } from '../pikku-request.js';
|
|
7
3
|
import { PikkuResponse } from '../pikku-response.js';
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Add middleware to a specific route or globally
|
|
6
|
+
*
|
|
7
|
+
* @param {APIMiddleware[] | string} routeOrMiddleware - Route pattern to match or middleware array
|
|
8
|
+
* @param {APIMiddleware} [middleware] - Middleware to add (required if first param is a string)
|
|
9
|
+
*/
|
|
10
|
+
export declare const addMiddleware: <APIMiddleware>(routeOrMiddleware: APIMiddleware[] | string, middleware?: APIMiddleware) => void;
|
|
11
|
+
/**
|
|
12
|
+
* Add a route to the global routes registry
|
|
13
|
+
*
|
|
14
|
+
* @param {CoreHTTPFunctionRoute} route - Route configuration to add
|
|
15
|
+
*/
|
|
16
|
+
export declare const addRoute: <In, Out, Route extends string, APIFunction, APIFunctionSessionless, APIPermission, APIMiddleware>(route: CoreHTTPFunctionRoute<In, Out, Route, APIFunction, APIFunctionSessionless, APIPermission, APIMiddleware>) => void;
|
|
17
|
+
/**
|
|
18
|
+
* Remove all routes from the global registry
|
|
19
|
+
*/
|
|
10
20
|
export declare const clearRoutes: () => void;
|
|
11
21
|
/**
|
|
22
|
+
* Set the HTTP routes metadata
|
|
23
|
+
*
|
|
24
|
+
* @param {HTTPRoutesMeta} routeMeta - Metadata for routes
|
|
12
25
|
* @ignore
|
|
13
26
|
*/
|
|
14
|
-
export declare const setHTTPRoutesMeta: (
|
|
27
|
+
export declare const setHTTPRoutesMeta: (routeMeta: HTTPRoutesMeta) => void;
|
|
15
28
|
/**
|
|
16
|
-
* Returns all the registered routes and associated metadata
|
|
29
|
+
* Returns all the registered routes and associated metadata
|
|
30
|
+
*
|
|
31
|
+
* @returns {Object} Object containing routes and routesMeta
|
|
17
32
|
* @internal
|
|
18
33
|
*/
|
|
19
34
|
export declare const getRoutes: () => {
|
|
20
35
|
routes: CoreHTTPFunctionRoute<any, any, any>[];
|
|
21
36
|
routesMeta: HTTPRoutesMeta;
|
|
22
37
|
};
|
|
23
|
-
|
|
24
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Create an HTTP interaction object from request and response
|
|
40
|
+
*
|
|
41
|
+
* @param {PikkuRequest | undefined} request - The HTTP request object
|
|
42
|
+
* @param {PikkuResponse | undefined} response - The HTTP response object
|
|
43
|
+
* @returns {PikkuHTTP | undefined} HTTP interaction object or undefined
|
|
44
|
+
*/
|
|
25
45
|
export declare const createHTTPInteraction: (request: PikkuRequest | undefined, response: PikkuResponse | undefined) => PikkuHTTP | undefined;
|
|
26
|
-
export declare const handleError: (e: any, http: PikkuHTTP | undefined, trackerId: string, logger: Logger, logWarningsForStatusCodes: number[], respondWith404: boolean, bubbleError: boolean) => void;
|
|
27
46
|
/**
|
|
47
|
+
* Run an HTTP route with the given parameters
|
|
48
|
+
*
|
|
49
|
+
* @param {Object} options - Options for running the route
|
|
50
|
+
* @returns {Promise<Out | void>} Result of the route handler
|
|
28
51
|
* @ignore
|
|
29
52
|
*/
|
|
30
53
|
export declare const runHTTPRoute: <In, Out>({ singletonServices, request, response, createSessionServices, route: apiRoute, method: apiType, skipUserSession, respondWith404, logWarningsForStatusCodes, coerceToArray, bubbleErrors, }: Pick<CoreHTTPFunctionRoute<unknown, unknown, any>, "route" | "method"> & RunRouteOptions & RunRouteParams<In>) => Promise<Out | void>;
|