@pikku/core 0.6.21 → 0.6.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/channel/channel-handler.d.ts +2 -2
  3. package/dist/channel/channel-handler.js +49 -26
  4. package/dist/channel/channel-runner.d.ts +3 -3
  5. package/dist/channel/channel-runner.js +5 -5
  6. package/dist/channel/channel.types.d.ts +12 -19
  7. package/dist/channel/local/local-channel-runner.d.ts +1 -1
  8. package/dist/channel/local/local-channel-runner.js +14 -8
  9. package/dist/channel/serverless/serverless-channel-runner.js +12 -7
  10. package/dist/handle-error.js +8 -6
  11. package/dist/http/http-route-runner.d.ts +75 -18
  12. package/dist/http/http-route-runner.js +148 -64
  13. package/dist/http/http-routes.types.d.ts +24 -9
  14. package/dist/http/incomingmessage-to-request-convertor.d.ts +1 -0
  15. package/dist/http/incomingmessage-to-request-convertor.js +1 -0
  16. package/dist/http/index.d.ts +4 -3
  17. package/dist/http/index.js +4 -3
  18. package/dist/http/{pikku-http-abstract-request.d.ts → pikku-fetch-http-request.d.ts} +14 -20
  19. package/dist/http/pikku-fetch-http-request.js +92 -0
  20. package/dist/http/pikku-fetch-http-response.d.ts +14 -0
  21. package/dist/http/pikku-fetch-http-response.js +59 -0
  22. package/dist/middleware/auth-apikey.js +4 -4
  23. package/dist/middleware/auth-bearer.js +4 -4
  24. package/dist/middleware/auth-cookie.js +16 -25
  25. package/dist/middleware-runner.d.ts +1 -1
  26. package/dist/pikku-request.d.ts +3 -3
  27. package/dist/pikku-request.js +5 -5
  28. package/dist/scheduler/scheduler-runner.d.ts +1 -1
  29. package/dist/services/user-session-service.d.ts +9 -14
  30. package/dist/services/user-session-service.js +22 -22
  31. package/dist/types/core.types.d.ts +1 -1
  32. package/dist/types/functions.types.d.ts +7 -2
  33. package/lcov.info +1529 -1412
  34. package/package.json +3 -4
  35. package/src/channel/channel-handler.ts +82 -29
  36. package/src/channel/channel-runner.ts +7 -7
  37. package/src/channel/channel.types.ts +19 -23
  38. package/src/channel/local/local-channel-runner.test.ts +68 -23
  39. package/src/channel/local/local-channel-runner.ts +23 -9
  40. package/src/channel/serverless/serverless-channel-runner.ts +15 -6
  41. package/src/handle-error.ts +8 -6
  42. package/src/http/http-route-runner.test.ts +13 -57
  43. package/src/http/http-route-runner.ts +173 -86
  44. package/src/http/http-routes.types.ts +26 -9
  45. package/src/http/incomingmessage-to-request-convertor.ts +0 -0
  46. package/src/http/index.ts +4 -5
  47. package/src/http/{pikku-http-abstract-request.ts → pikku-fetch-http-request.ts} +48 -39
  48. package/src/http/pikku-fetch-http-response.ts +70 -0
  49. package/src/middleware/auth-apikey.ts +4 -4
  50. package/src/middleware/auth-bearer.ts +4 -4
  51. package/src/middleware/auth-cookie.ts +20 -28
  52. package/src/middleware-runner.ts +1 -1
  53. package/src/pikku-request.ts +8 -4
  54. package/src/scheduler/scheduler-runner.ts +0 -2
  55. package/src/services/user-session-service.ts +26 -28
  56. package/src/types/core.types.ts +1 -1
  57. package/src/types/functions.types.ts +19 -4
  58. package/tsconfig.tsbuildinfo +1 -1
  59. package/dist/http/pikku-http-abstract-request.js +0 -76
  60. package/dist/http/pikku-http-abstract-response.d.ts +0 -58
  61. package/dist/http/pikku-http-abstract-response.js +0 -54
  62. package/src/http/pikku-http-abstract-response.ts +0 -79
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  ## 0.6
2
2
 
3
+ ## 0.6.23
4
+
5
+ ### Patch Changes
6
+
7
+ - 1c8c470: fix: await schema validation
8
+
9
+ ## 0.6.22
10
+
11
+ ### Patch Changes
12
+
13
+ - 60b2265: refactor: supporting request and response objects
14
+
3
15
  ## 0.6.21
4
16
 
5
17
  ### Patch Changes
@@ -1,3 +1,3 @@
1
- import { CoreServices } from '../types/core.types.js';
1
+ import { CoreServices, CoreUserSession } 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) => (rawData: any) => Promise<unknown>;
3
+ export declare const processMessageHandlers: (services: CoreServices, session: CoreUserSession | undefined, channelConfig: CoreAPIChannel<any, any>, channelHandler: PikkuChannelHandler) => (rawData: any) => Promise<unknown>;
@@ -1,45 +1,65 @@
1
1
  import { verifyPermissions } from '../permissions.js';
2
2
  import { pikkuState } from '../pikku-state.js';
3
- const validateSchema = (logger, data, channelName, routingProperty, routerValue) => {
3
+ const getRouteMeta = (channelName, routingProperty, routerValue) => {
4
4
  const channelsMeta = pikkuState('channel', 'meta');
5
- for (const channelMeta of channelsMeta) {
6
- if (routingProperty && routerValue) {
7
- const channelRoute = channelMeta.messageRoutes[routingProperty]?.[routerValue];
8
- if (channelRoute) {
9
- const schemaNames = channelRoute.inputs;
10
- if (schemaNames) {
11
- // TODO
12
- // loadSchema(schemaNames, logger)
13
- // validateJson(schemaNames, data)
14
- }
15
- return;
16
- }
5
+ const channelMeta = channelsMeta.find((channelMeta) => channelMeta.name === channelName);
6
+ if (!channelMeta) {
7
+ throw new Error(`Channel ${channelName} not found`);
8
+ }
9
+ if (!routingProperty) {
10
+ if (!channelMeta.message) {
11
+ throw new Error(`Channel ${channelName} has no default message route`);
17
12
  }
13
+ return channelMeta.message;
14
+ }
15
+ if (!routerValue) {
16
+ throw new Error(`Channel ${channelName} requires a router value for ${routingProperty}`);
17
+ }
18
+ const route = channelMeta.messageRoutes[routingProperty]?.[routerValue];
19
+ if (!route) {
20
+ throw new Error(`Channel ${channelName} has no route for ${routingProperty}:${routerValue}`);
18
21
  }
22
+ return route;
19
23
  };
20
- const validateAuth = (requiresSession, userSession, onMessage) => {
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
+ const validateAuth = (requiresSession, session, onMessage) => {
21
33
  const auth = typeof onMessage === 'function'
22
34
  ? requiresSession
23
35
  : onMessage.auth === undefined
24
36
  ? requiresSession
25
37
  : onMessage.auth;
26
- if (auth && !userSession) {
38
+ if (auth && !session) {
27
39
  return false;
28
40
  }
29
41
  return true;
30
42
  };
31
- const validatePermissions = async (services, userSession, onMessage, data) => {
43
+ const validatePermissions = async (services, session, onMessage, data) => {
32
44
  const permissions = typeof onMessage === 'function' ? {} : onMessage.permissions;
33
- return await verifyPermissions(permissions, services, data, userSession);
45
+ return await verifyPermissions(permissions, services, data, session);
34
46
  };
35
- const runFunction = async (services, channelHandler, onMessage, data) => {
47
+ const runFunction = async (services, channelHandler, channelMessageMeta, session, onMessage, data) => {
36
48
  const func = typeof onMessage === 'function' ? onMessage : onMessage.func;
37
- return await func(services, channelHandler.getChannel(), data);
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
+ }
38
58
  };
39
- export const processMessageHandlers = (services, channelConfig, channelHandler) => {
59
+ export const processMessageHandlers = (services, session, channelConfig, channelHandler) => {
40
60
  const logger = services.logger;
41
61
  const requiresSession = channelConfig.auth !== false;
42
- const processMessage = async (data, onMessage, routingProperty, routerValue) => {
62
+ const processMessage = async (data, onMessage, session, routingProperty, routerValue) => {
43
63
  if (!validateAuth(requiresSession, channelHandler, onMessage)) {
44
64
  const routeMessage = routingProperty
45
65
  ? `route '${routingProperty}:${routerValue}'`
@@ -49,12 +69,15 @@ export const processMessageHandlers = (services, channelConfig, channelHandler)
49
69
  channelHandler.getChannel().send(`Unauthorized for ${routeMessage}`);
50
70
  return;
51
71
  }
52
- validateSchema(services.logger, data, channelConfig.name, routingProperty, routerValue);
72
+ const routeMeta = getRouteMeta(channelConfig.name, routingProperty, routerValue);
73
+ if (routeMeta) {
74
+ await validateSchema(services.logger, data, routeMeta);
75
+ }
53
76
  const hasPermission = await validatePermissions(services, channelHandler, onMessage, data);
54
77
  if (!hasPermission) {
55
78
  logger.error(`Channel ${channelConfig.name} requires permissions for ${routingProperty || 'default message route'}`);
56
79
  }
57
- return await runFunction(services, channelHandler, onMessage, data);
80
+ return await runFunction(services, channelHandler, routeMeta, session, onMessage, data);
58
81
  };
59
82
  const onMessage = async (rawData) => {
60
83
  let result;
@@ -68,14 +91,14 @@ export const processMessageHandlers = (services, channelConfig, channelHandler)
68
91
  const routerValue = messageData[routingProperty];
69
92
  if (routerValue && routes[routerValue]) {
70
93
  processed = true;
71
- result = await processMessage(messageData, routes[routerValue], routingProperty, routerValue);
94
+ result = await processMessage(messageData, routes[routerValue], session, routingProperty, routerValue);
72
95
  break;
73
96
  }
74
97
  }
75
98
  // Default handler if no routes matched but json data was parsed
76
99
  if (!processed && channelConfig.onMessage) {
77
100
  processed = true;
78
- result = await processMessage(messageData, channelConfig.onMessage);
101
+ result = await processMessage(messageData, channelConfig.onMessage, session);
79
102
  }
80
103
  }
81
104
  catch (error) {
@@ -85,7 +108,7 @@ export const processMessageHandlers = (services, channelConfig, channelHandler)
85
108
  // Default handler if no routes matched and json data wasn't parsed
86
109
  if (!processed && channelConfig.onMessage) {
87
110
  processed = true;
88
- result = await processMessage(rawData, channelConfig.onMessage);
111
+ result = await processMessage(rawData, channelConfig.onMessage, session);
89
112
  }
90
113
  if (!processed) {
91
114
  logger.error(`No handler found for message in channel ${channelConfig.name} for ${rawData}`);
@@ -4,11 +4,11 @@ export declare const addChannel: <In, Channel extends string, ChannelFunction, C
4
4
  export declare const getMatchingChannelConfig: (request: string) => {
5
5
  matchedPath: import("path-to-regexp").MatchResult<Partial<Record<string, string | string[]>>>;
6
6
  params: Partial<Record<string, string | string[]>>;
7
- 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>>;
7
+ 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("../index.js").CoreAPIFunction<unknown, unknown>, import("./channel.types.js").CoreChannelMessage<unknown, unknown, unknown, import("../index.js").CoreServices> | import("../index.js").CoreAPIFunction<unknown, unknown>, import("../index.js").CoreAPIPermission<any>>;
8
8
  schemaName: string | null | undefined;
9
9
  } | null;
10
- export declare const openChannel: ({ route, singletonServices, coerceToArray, http, }: Pick<CoreAPIChannel<unknown, string>, "route"> & Omit<RunChannelParams<unknown>, "response" | "request"> & {
11
- userSessionService?: UserSessionService<any>;
10
+ export declare const openChannel: ({ route, singletonServices, coerceToArray, request, }: Pick<CoreAPIChannel<unknown, string>, "route"> & RunChannelParams<unknown> & {
11
+ userSessionService: UserSessionService<any>;
12
12
  } & RunChannelOptions) => Promise<{
13
13
  openingData: unknown;
14
14
  channelConfig: CoreAPIChannel<unknown, any>;
@@ -27,7 +27,7 @@ export const getMatchingChannelConfig = (request) => {
27
27
  }
28
28
  return null;
29
29
  };
30
- export const openChannel = async ({ route, singletonServices, coerceToArray = false, http, }) => {
30
+ export const openChannel = async ({ route, singletonServices, coerceToArray = false, request, }) => {
31
31
  const matchingChannel = getMatchingChannelConfig(route);
32
32
  if (!matchingChannel) {
33
33
  singletonServices.logger.info(`Channel not found: ${route}`);
@@ -35,15 +35,15 @@ export const openChannel = async ({ route, singletonServices, coerceToArray = fa
35
35
  }
36
36
  const { params, channelConfig, schemaName } = matchingChannel;
37
37
  const requiresSession = channelConfig.auth !== false;
38
- http?.request?.setParams(params);
38
+ request?.setParams(params);
39
39
  singletonServices.logger.info(`Matched channel: ${channelConfig.name} | route: ${channelConfig.route} | auth: ${requiresSession.toString()}`);
40
40
  let openingData;
41
- if (http?.request) {
42
- openingData = await http.request.getData();
41
+ if (request) {
42
+ openingData = await request.data();
43
43
  if (coerceToArray && schemaName) {
44
44
  coerceQueryStringToArray(schemaName, openingData);
45
45
  }
46
- validateSchema(singletonServices.logger, singletonServices.schemaService, schemaName, openingData);
46
+ await validateSchema(singletonServices.logger, singletonServices.schemaService, schemaName, openingData);
47
47
  }
48
48
  return { openingData, channelConfig };
49
49
  };
@@ -1,11 +1,7 @@
1
1
  import { PikkuError } from '../errors/error-handler.js';
2
- import { HTTPFunctionMetaInputTypes, PikkuHTTP } from '../http/http-routes.types.js';
3
- import { PikkuHTTPAbstractRequest } from '../http/pikku-http-abstract-request.js';
4
- import { PikkuHTTPAbstractResponse } from '../http/pikku-http-abstract-response.js';
2
+ import { HTTPFunctionMetaInputTypes, PikkuHTTPRequest, PikkuHTTPResponse } from '../http/http-routes.types.js';
5
3
  import { APIDocs, CoreServices, CoreSingletonServices, CreateSessionServices, PikkuMiddleware } from '../types/core.types.js';
6
- import { CoreAPIPermission } from '../types/functions.types.js';
7
- import { PikkuRequest } from '../pikku-request.js';
8
- import { PikkuResponse } from '../pikku-response.js';
4
+ import { CoreAPIFunction, CoreAPIPermission } from '../types/functions.types.js';
9
5
  export type RunChannelOptions = Partial<{
10
6
  skipUserSession: boolean;
11
7
  respondWith404: boolean;
@@ -16,12 +12,15 @@ export type RunChannelOptions = Partial<{
16
12
  export type RunChannelParams<ChannelData> = {
17
13
  channelId: string;
18
14
  singletonServices: CoreSingletonServices;
19
- request?: PikkuRequest<ChannelData> | PikkuHTTPAbstractRequest<ChannelData>;
20
- response?: PikkuResponse | PikkuHTTPAbstractResponse;
21
- http?: PikkuHTTP;
15
+ request?: PikkuHTTPRequest<ChannelData>;
16
+ response?: PikkuHTTPResponse;
22
17
  createSessionServices?: CreateSessionServices;
23
18
  };
24
- export interface HandlerMeta {
19
+ export interface ChannelMessageMeta {
20
+ inputs: string[] | null;
21
+ outputs: string[] | null;
22
+ docs?: APIDocs;
23
+ type: null | string;
25
24
  }
26
25
  export interface ChannelMeta {
27
26
  name: string;
@@ -32,14 +31,8 @@ export interface ChannelMeta {
32
31
  inputTypes?: HTTPFunctionMetaInputTypes;
33
32
  connect: boolean;
34
33
  disconnect: boolean;
35
- message: {
36
- inputs: string[] | null;
37
- outputs: string[] | null;
38
- } | null;
39
- messageRoutes: Record<string, Record<string, {
40
- inputs: string[] | null;
41
- outputs: string[] | null;
42
- }>>;
34
+ message: ChannelMessageMeta | null;
35
+ messageRoutes: Record<string, Record<string, ChannelMessageMeta>>;
43
36
  docs?: APIDocs;
44
37
  tags?: string[];
45
38
  }
@@ -58,7 +51,7 @@ export type CoreAPIChannelMessage<ChannelFunctionMessage = CoreChannelMessage<un
58
51
  func: ChannelFunctionMessage;
59
52
  route: string;
60
53
  };
61
- export type CoreAPIChannel<ChannelData, Channel extends string, ChannelFunctionConnection = CoreChannelConnection<ChannelData>, ChannelFunctionDisconnection = CoreChannelDisconnection<ChannelData>, ChannelFunctionDefaultMessage = CoreChannelMessage<unknown, unknown, unknown>, ChannelFunctionMessageRoute = CoreChannelMessage<unknown, unknown, unknown>, APIPermission = CoreAPIPermission<ChannelData>> = {
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>> = {
62
55
  name: string;
63
56
  route: Channel;
64
57
  onConnect?: ChannelFunctionConnection;
@@ -1,3 +1,3 @@
1
1
  import { CoreAPIChannel, RunChannelOptions, RunChannelParams } from '../channel.types.js';
2
2
  import { PikkuLocalChannelHandler } from './local-channel-handler.js';
3
- export declare const runLocalChannel: ({ singletonServices, channelId, request, response, route, createSessionServices, skipUserSession, respondWith404, coerceToArray, logWarningsForStatusCodes, bubbleErrors, }: Pick<CoreAPIChannel<unknown, any>, "route"> & RunChannelOptions & RunChannelParams<unknown>) => Promise<PikkuLocalChannelHandler | void>;
3
+ export declare const runLocalChannel: ({ singletonServices, channelId, request, response, route, createSessionServices, skipUserSession, respondWith404, coerceToArray, logWarningsForStatusCodes, bubbleErrors, }: Partial<Pick<CoreAPIChannel<unknown, any>, "route">> & RunChannelOptions & RunChannelParams<unknown>) => Promise<PikkuLocalChannelHandler | void>;
@@ -5,29 +5,35 @@ 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
- import { LocalUserSessionService } from '../../services/user-session-service.js';
8
+ import { PikkuUserSessionService } from '../../services/user-session-service.js';
9
9
  export const runLocalChannel = async ({ singletonServices, channelId, request, response, route, createSessionServices, skipUserSession = false, respondWith404 = true, coerceToArray = false, logWarningsForStatusCodes = [], bubbleErrors = false, }) => {
10
10
  let sessionServices;
11
11
  let channelHandler;
12
- const userSessionService = new LocalUserSessionService();
13
- const http = createHTTPInteraction(request, response);
12
+ const userSessionService = new PikkuUserSessionService();
13
+ let http;
14
+ if (request) {
15
+ http = createHTTPInteraction(request, response);
16
+ route = http?.request?.path();
17
+ }
14
18
  const main = async () => {
15
19
  try {
16
20
  const { openingData, channelConfig } = await openChannel({
17
21
  channelId,
18
22
  createSessionServices,
19
23
  respondWith404,
20
- http,
24
+ request,
25
+ response,
21
26
  route,
22
27
  singletonServices,
23
28
  skipUserSession,
24
29
  coerceToArray,
30
+ userSessionService,
25
31
  });
26
32
  channelHandler = new PikkuLocalChannelHandler(channelId, channelConfig.name, openingData);
27
33
  const channel = channelHandler.getChannel();
28
- const userSession = await userSessionService.get();
34
+ const session = await userSessionService.get();
29
35
  if (createSessionServices) {
30
- sessionServices = await createSessionServices(singletonServices, { http }, userSession);
36
+ sessionServices = await createSessionServices(singletonServices, { http }, session);
31
37
  }
32
38
  const allServices = {
33
39
  ...singletonServices,
@@ -43,7 +49,7 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
43
49
  await closeSessionServices(singletonServices.logger, sessionServices);
44
50
  }
45
51
  });
46
- channelHandler.registerOnMessage(processMessageHandlers(allServices, channelConfig, channelHandler));
52
+ channelHandler.registerOnMessage(processMessageHandlers(allServices, session, channelConfig, channelHandler));
47
53
  }
48
54
  catch (e) {
49
55
  handleError(e, http, channelId, singletonServices.logger, logWarningsForStatusCodes, respondWith404, bubbleErrors);
@@ -56,7 +62,7 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
56
62
  };
57
63
  await runMiddleware({
58
64
  ...singletonServices,
59
- userSession: userSessionService,
65
+ userSessionService,
60
66
  }, { http }, route.middleware || [], main);
61
67
  return channelHandler;
62
68
  };
@@ -3,9 +3,10 @@ import { processMessageHandlers } from '../channel-handler.js';
3
3
  import { openChannel } from '../channel-runner.js';
4
4
  import { createHTTPInteraction } from '../../http/http-route-runner.js';
5
5
  import { handleError } from '../../handle-error.js';
6
- import { RemoteUserSessionService } from '../../services/user-session-service.js';
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
+ import { PikkuFetchHTTPRequest } from '../../http/pikku-fetch-http-request.js';
9
10
  const getVariablesForChannel = ({ channelId, channelName, channelHandlerFactory, openingData, }) => {
10
11
  const channels = pikkuState('channel', 'channels');
11
12
  const channelConfig = channels.find((channelConfig) => channelConfig.name === channelName);
@@ -21,15 +22,19 @@ const getVariablesForChannel = ({ channelId, channelName, channelHandlerFactory,
21
22
  };
22
23
  export const runChannelConnect = async ({ singletonServices, channelId, channelObject, request, response, route, createSessionServices, channelStore, channelHandlerFactory, coerceToArray = false, logWarningsForStatusCodes = [], respondWith404 = true, bubbleErrors = false, }) => {
23
24
  let sessionServices;
24
- const http = createHTTPInteraction(request, response);
25
- const userSessionService = new RemoteUserSessionService(channelStore, channelId);
25
+ let http;
26
+ if (request instanceof Request) {
27
+ http = createHTTPInteraction(new PikkuFetchHTTPRequest(request), response);
28
+ }
29
+ const userSessionService = new PikkuUserSessionService(channelStore, channelId);
26
30
  const { channelConfig, openingData } = await openChannel({
27
31
  channelId,
28
32
  createSessionServices,
29
- http,
33
+ request,
30
34
  route,
31
35
  singletonServices,
32
36
  coerceToArray,
37
+ userSessionService,
33
38
  });
34
39
  const main = async () => {
35
40
  try {
@@ -48,7 +53,7 @@ export const runChannelConnect = async ({ singletonServices, channelId, channelO
48
53
  sessionServices = await createSessionServices(singletonServices, { http }, await userSessionService.get());
49
54
  }
50
55
  await channelConfig.onConnect?.({ ...singletonServices, ...sessionServices }, channel);
51
- http?.response?.setStatus(101);
56
+ http?.response?.status(101);
52
57
  }
53
58
  catch (e) {
54
59
  handleError(e, http, channelId, singletonServices.logger, logWarningsForStatusCodes, respondWith404, bubbleErrors);
@@ -61,7 +66,7 @@ export const runChannelConnect = async ({ singletonServices, channelId, channelO
61
66
  };
62
67
  await runMiddleware({
63
68
  ...singletonServices,
64
- userSession: userSessionService,
69
+ userSessionService,
65
70
  }, { http }, channelConfig.middleware || [], main);
66
71
  };
67
72
  export const runChannelDisconnect = async ({ singletonServices, ...params }) => {
@@ -94,7 +99,7 @@ export const runChannelMessage = async ({ singletonServices, ...params }, data)
94
99
  }
95
100
  let response;
96
101
  try {
97
- const onMessage = processMessageHandlers({ ...singletonServices, ...sessionServices }, channelConfig, channelHandler);
102
+ const onMessage = processMessageHandlers({ ...singletonServices, ...sessionServices }, session, channelConfig, channelHandler);
98
103
  response = await onMessage(data);
99
104
  }
100
105
  finally {
@@ -20,8 +20,8 @@ export const handleError = (e, http, trackerId, logger, logWarningsForStatusCode
20
20
  const errorResponse = getErrorResponse(e);
21
21
  if (errorResponse != null) {
22
22
  // Set status and response body
23
- http?.response?.setStatus(errorResponse.status);
24
- http?.response?.setJson({
23
+ http?.response?.status(errorResponse.status);
24
+ http?.response?.json({
25
25
  message: errorResponse.message,
26
26
  payload: e.payload,
27
27
  traceId: trackerId,
@@ -37,21 +37,23 @@ export const handleError = (e, http, trackerId, logger, logWarningsForStatusCode
37
37
  else {
38
38
  // Handle unexpected errors
39
39
  logger.error(e);
40
- http?.response?.setStatus(500);
40
+ http?.response?.status(500);
41
41
  if (trackerId) {
42
42
  logger.warn(`Error id: ${trackerId}`);
43
- http?.response?.setJson({ errorId: trackerId });
43
+ http?.response?.json({ errorId: trackerId });
44
44
  }
45
45
  }
46
46
  // Handle 404 errors specifically
47
47
  if (e instanceof NotFoundError) {
48
- http?.response?.end();
48
+ // TODO
49
+ // http?.response?.end()
49
50
  }
50
51
  // Either bubble up or end the response
51
52
  if (bubbleError) {
52
53
  throw e;
53
54
  }
54
55
  else {
55
- http?.response?.end();
56
+ // TODO
57
+ // http?.response?.end()
56
58
  }
57
59
  };
@@ -1,33 +1,90 @@
1
- import { CoreHTTPFunctionRoute, RunRouteOptions, RunRouteParams, PikkuHTTP } from './http-routes.types.js';
1
+ import { CoreHTTPFunctionRoute, RunRouteOptions, RunRouteParams, PikkuHTTP, PikkuHTTPRequest, PikkuHTTPResponse } from './http-routes.types.js';
2
2
  import { PikkuMiddleware } from '../types/core.types.js';
3
- import { PikkuRequest } from '../pikku-request.js';
4
- import { PikkuResponse } from '../pikku-response.js';
3
+ import { PikkuFetchHTTPResponse } from './pikku-fetch-http-response.js';
5
4
  /**
6
- * Add middleware to a specific route or globally
5
+ * Registers middleware either globally or for a specific route.
7
6
  *
8
- * @param {APIMiddleware[] | string} routeOrMiddleware - Route pattern to match or middleware array
9
- * @param {APIMiddleware} [middleware] - Middleware to add (required if first param is a string)
7
+ * When a string route pattern is provided along with middleware, the middleware
8
+ * is applied only to that route. Otherwise, if an array is provided, it is treated
9
+ * as global middleware (applied to all routes).
10
+ *
11
+ * @template APIMiddleware The middleware type.
12
+ * @param {APIMiddleware[] | string} routeOrMiddleware - Either a global middleware array or a route pattern string.
13
+ * @param {APIMiddleware[]} [middleware] - The middleware array to apply when a route pattern is specified.
10
14
  */
11
15
  export declare const addMiddleware: <APIMiddleware extends PikkuMiddleware>(routeOrMiddleware: APIMiddleware[] | string, middleware?: APIMiddleware[]) => void;
12
16
  /**
13
- * Add a route to the global routes registry
17
+ * Adds a new route to the global HTTP route registry.
18
+ *
19
+ * The route configuration includes the HTTP method, route path, permissions,
20
+ * middleware, and the handler function that implements the route's logic.
14
21
  *
15
- * @param {CoreHTTPFunctionRoute} route - Route configuration to add
22
+ * @template In Expected input type.
23
+ * @template Out Expected output type.
24
+ * @template Route Route pattern as a string.
25
+ * @template APIFunction Type for the route handler function.
26
+ * @template APIFunctionSessionless Type for a sessionless handler.
27
+ * @template APIPermission Type representing required permissions.
28
+ * @template APIMiddleware Middleware type to be used with the route.
29
+ * @param {CoreHTTPFunctionRoute<In, Out, Route, APIFunction, APIFunctionSessionless, APIPermission, APIMiddleware>} route - The route configuration object.
16
30
  */
17
31
  export declare const addRoute: <In, Out, Route extends string, APIFunction, APIFunctionSessionless, APIPermission, APIMiddleware>(route: CoreHTTPFunctionRoute<In, Out, Route, APIFunction, APIFunctionSessionless, APIPermission, APIMiddleware>) => void;
18
32
  /**
19
- * Create an HTTP interaction object from request and response
33
+ * Combines the request and response objects into a single HTTP interaction object.
34
+ *
35
+ * This utility function creates an object that holds both the HTTP request and response,
36
+ * which simplifies passing these around through middleware and route execution.
37
+ *
38
+ * @param {PikkuHTTPRequest | undefined} request - The HTTP request object.
39
+ * @param {PikkuHTTPResponse | undefined} response - The HTTP response object.
40
+ * @returns {PikkuHTTP | undefined} The combined HTTP interaction object or undefined if none provided.
41
+ */
42
+ export declare const createHTTPInteraction: (request: PikkuHTTPRequest | undefined, response: PikkuHTTPResponse | undefined) => PikkuHTTP | undefined;
43
+ /**
44
+ * Executes an HTTP route for a given Fetch API request.
20
45
  *
21
- * @param {PikkuRequest | undefined} request - The HTTP request object
22
- * @param {PikkuResponse | undefined} response - The HTTP response object
23
- * @returns {PikkuHTTP | undefined} HTTP interaction object or undefined
46
+ * This function wraps the entire lifecycle of handling an HTTP request:
47
+ * - Matching the request to a registered route.
48
+ * - Validating input and session state.
49
+ * - Running middleware and the route handler.
50
+ * - Handling errors and forming the response.
51
+ *
52
+ * @template In Expected input data type.
53
+ * @template Out Expected output data type.
54
+ * @param {Request} request - The native Fetch API Request object.
55
+ * @param {RunRouteOptions & RunRouteParams} params - Additional options including services and session management.
56
+ * @returns {Promise<Response>} A promise that resolves to a Fetch API Response object.
57
+ */
58
+ export declare const fetch: <In, Out>(request: Request, params: RunRouteOptions & RunRouteParams) => Promise<Response>;
59
+ /**
60
+ * Executes an HTTP route using a Pikku-specific request wrapper.
61
+ *
62
+ * This variant accepts either a native Request or a PikkuHTTPRequest object and returns
63
+ * a PikkuFetchHTTPResponse for further manipulation if needed.
64
+ *
65
+ * @template In Expected input data type.
66
+ * @template Out Expected output data type.
67
+ * @param {Request | PikkuHTTPRequest} request - The request object.
68
+ * @param {RunRouteOptions & RunRouteParams} params - Execution options including services and session configuration.
69
+ * @returns {Promise<PikkuFetchHTTPResponse>} A promise that resolves to a PikkuFetchHTTPResponse object.
24
70
  */
25
- export declare const createHTTPInteraction: (request: PikkuRequest | undefined, response: PikkuResponse | undefined) => PikkuHTTP | undefined;
71
+ export declare const pikkuFetch: <In, Out>(request: Request | PikkuHTTPRequest, params: RunRouteOptions & RunRouteParams) => Promise<PikkuFetchHTTPResponse>;
26
72
  /**
27
- * Run an HTTP route with the given parameters
73
+ * Core function to process an HTTP request through route matching, validation,
74
+ * middleware execution, error handling, and session service cleanup.
75
+ *
76
+ * This function does the following:
77
+ * - Wraps the incoming request and response into an HTTP interaction object.
78
+ * - Determines the correct route based on HTTP method and path.
79
+ * - Executes middleware and the route handler.
80
+ * - Catches and handles errors, optionally bubbling them if configured.
81
+ * - Cleans up any session services created during processing.
28
82
  *
29
- * @param {Object} options - Options for running the route
30
- * @returns {Promise<Out | void>} Result of the route handler
31
- * @ignore
83
+ * @template In Expected input data type.
84
+ * @template Out Expected output data type.
85
+ * @param {Request | PikkuHTTPRequest} request - The incoming HTTP request.
86
+ * @param {PikkuHTTPResponse} response - The response object to be populated.
87
+ * @param {RunRouteOptions & RunRouteParams} options - Options such as singleton services, session handling, and error configuration.
88
+ * @returns {Promise<Out | void>} The output from the route handler or void if an error occurred.
32
89
  */
33
- 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>;
90
+ export declare const fetchData: <In, Out>(request: Request | PikkuHTTPRequest, response: PikkuHTTPResponse, { singletonServices, createSessionServices, skipUserSession, respondWith404, logWarningsForStatusCodes, coerceToArray, bubbleErrors, }: RunRouteOptions & RunRouteParams) => Promise<Out | void>;