@pikku/core 0.6.15 → 0.6.17

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 (92) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/channel/channel-handler.js +2 -2
  3. package/dist/channel/channel-runner.d.ts +2 -14
  4. package/dist/channel/channel-runner.js +5 -36
  5. package/dist/channel/channel-store.d.ts +9 -6
  6. package/dist/channel/index.d.ts +0 -1
  7. package/dist/channel/index.js +0 -1
  8. package/dist/channel/local/local-channel-runner.js +2 -2
  9. package/dist/channel/log-channels.js +2 -2
  10. package/dist/channel/serverless/serverless-channel-runner.js +8 -7
  11. package/dist/errors/error-handler.d.ts +1 -1
  12. package/dist/errors/error-handler.js +6 -14
  13. package/dist/errors/errors.d.ts +1 -1
  14. package/dist/errors/errors.js +2 -2
  15. package/dist/http/http-route-runner.d.ts +3 -23
  16. package/dist/http/http-route-runner.js +13 -82
  17. package/dist/http/index.d.ts +1 -1
  18. package/dist/http/index.js +1 -1
  19. package/dist/http/log-http-routes.js +2 -2
  20. package/dist/index.d.ts +1 -0
  21. package/dist/index.js +1 -0
  22. package/dist/middleware/auth-apikey.d.ts +15 -0
  23. package/dist/middleware/auth-apikey.js +36 -0
  24. package/dist/middleware/auth-bearer.d.ts +12 -0
  25. package/dist/middleware/auth-bearer.js +40 -0
  26. package/dist/middleware/auth-cookie.d.ts +16 -0
  27. package/dist/middleware/auth-cookie.js +43 -0
  28. package/dist/middleware/index.d.ts +3 -4
  29. package/dist/middleware/index.js +3 -4
  30. package/dist/middleware/timeout.d.ts +2 -0
  31. package/dist/middleware/timeout.js +10 -0
  32. package/dist/middleware-runner.d.ts +1 -1
  33. package/dist/pikku-state.d.ts +29 -0
  34. package/dist/pikku-state.js +30 -0
  35. package/dist/scheduler/index.d.ts +1 -1
  36. package/dist/scheduler/index.js +1 -1
  37. package/dist/scheduler/log-schedulers.js +2 -2
  38. package/dist/scheduler/scheduler-runner.d.ts +1 -11
  39. package/dist/scheduler/scheduler-runner.js +6 -33
  40. package/dist/schema.js +2 -2
  41. package/dist/services/jwt-service.d.ts +1 -10
  42. package/dist/services/user-session-service.d.ts +2 -2
  43. package/dist/services/user-session-service.js +6 -6
  44. package/dist/types/core.types.d.ts +3 -3
  45. package/lcov.info +820 -913
  46. package/package.json +2 -1
  47. package/src/channel/channel-handler.ts +2 -2
  48. package/src/channel/channel-runner.ts +4 -43
  49. package/src/channel/channel-store.ts +8 -11
  50. package/src/channel/index.ts +0 -1
  51. package/src/channel/local/local-channel-runner.test.ts +10 -20
  52. package/src/channel/local/local-channel-runner.ts +2 -2
  53. package/src/channel/log-channels.ts +2 -2
  54. package/src/channel/serverless/serverless-channel-runner.ts +10 -9
  55. package/src/errors/error-handler.ts +7 -18
  56. package/src/errors/errors.ts +2 -3
  57. package/src/handle-error.ts +0 -1
  58. package/src/http/http-route-runner.test.ts +3 -2
  59. package/src/http/http-route-runner.ts +18 -99
  60. package/src/http/index.ts +1 -6
  61. package/src/http/log-http-routes.ts +2 -2
  62. package/src/index.ts +1 -0
  63. package/src/middleware/auth-apikey.ts +65 -0
  64. package/src/middleware/auth-bearer.ts +65 -0
  65. package/src/middleware/auth-cookie.ts +76 -0
  66. package/src/middleware/index.ts +3 -4
  67. package/src/middleware/timeout.ts +13 -0
  68. package/src/middleware-runner.ts +1 -1
  69. package/src/pikku-state.ts +70 -0
  70. package/src/scheduler/index.ts +1 -7
  71. package/src/scheduler/log-schedulers.ts +2 -2
  72. package/src/scheduler/scheduler-runner.ts +7 -46
  73. package/src/schema.ts +2 -2
  74. package/src/services/jwt-service.ts +1 -12
  75. package/src/services/user-session-service.ts +8 -6
  76. package/src/types/core.types.ts +3 -6
  77. package/tsconfig.tsbuildinfo +1 -1
  78. package/dist/channel/eventhub-forwarder.d.ts +0 -17
  79. package/dist/channel/eventhub-forwarder.js +0 -1
  80. package/dist/middleware/auth-api-key.d.ts +0 -9
  81. package/dist/middleware/auth-api-key.js +0 -23
  82. package/dist/middleware/auth-cookie-middleware.d.ts +0 -11
  83. package/dist/middleware/auth-cookie-middleware.js +0 -36
  84. package/dist/middleware/auth-jwt-middleware.d.ts +0 -10
  85. package/dist/middleware/auth-jwt-middleware.js +0 -32
  86. package/dist/middleware/auth-query-middleware.d.ts +0 -10
  87. package/dist/middleware/auth-query-middleware.js +0 -21
  88. package/src/channel/eventhub-forwarder.ts +0 -25
  89. package/src/middleware/auth-api-key.ts +0 -32
  90. package/src/middleware/auth-cookie-middleware.ts +0 -54
  91. package/src/middleware/auth-jwt-middleware.ts +0 -47
  92. package/src/middleware/auth-query-middleware.ts +0 -33
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  ## 0.6
2
2
 
3
+ ## 0.6.17
4
+
5
+ ### Patch Changes
6
+
7
+ - ebc04eb: refactor: move all global state into pikku state
8
+ - 8a14f3a: refactor: removing user session from channel object
9
+ - 2c47386: refactor: improving middleware
10
+
11
+ ## 0.6.16
12
+
13
+ ### Patch Changes
14
+
15
+ - 3cbdf9e: fix: adding missing crypto import
16
+
3
17
  ## 0.6.15
4
18
 
5
19
  ### Patch Changes
@@ -1,7 +1,7 @@
1
1
  import { verifyPermissions } from '../permissions.js';
2
- import { getChannels } from './channel-runner.js';
2
+ import { pikkuState } from '../pikku-state.js';
3
3
  const validateSchema = (logger, data, channelName, routingProperty, routerValue) => {
4
- const { channelsMeta } = getChannels();
4
+ const channelsMeta = pikkuState('channel', 'meta');
5
5
  for (const channelMeta of channelsMeta) {
6
6
  if (routingProperty && routerValue) {
7
7
  const channelRoute = channelMeta.messageRoutes[routingProperty]?.[routerValue];
@@ -1,25 +1,13 @@
1
1
  import { UserSessionService } from '../services/user-session-service.js';
2
- import { CoreAPIChannel, ChannelsMeta, CoreAPIChannels, RunChannelOptions, RunChannelParams } from './channel.types.js';
3
- /**
4
- * Returns all the registered routes and associated metadata.
5
- * @internal
6
- */
7
- export declare const getChannels: () => {
8
- channels: CoreAPIChannels;
9
- channelsMeta: ChannelsMeta;
10
- };
2
+ import { CoreAPIChannel, RunChannelOptions, RunChannelParams } from './channel.types.js';
11
3
  export declare const addChannel: <In, Channel extends string, ChannelFunction, ChannelFunctionSessionless, APIPermission>(channel: CoreAPIChannel<In, Channel, ChannelFunction, ChannelFunctionSessionless, APIPermission>) => void;
12
- /**
13
- * @ignore
14
- */
15
- export declare const setChannelsMeta: (_channelsMeta: ChannelsMeta) => void;
16
4
  export declare const getMatchingChannelConfig: (request: string) => {
17
5
  matchedPath: import("path-to-regexp").MatchResult<Partial<Record<string, string | string[]>>>;
18
6
  params: Partial<Record<string, string | string[]>>;
19
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>>;
20
8
  schemaName: string | null | undefined;
21
9
  } | null;
22
- export declare const openChannel: ({ route, singletonServices, coerceToArray, http, userSessionService, }: Pick<CoreAPIChannel<unknown, string>, "route"> & Omit<RunChannelParams<unknown>, "response" | "request"> & {
10
+ export declare const openChannel: ({ route, singletonServices, coerceToArray, http, }: Pick<CoreAPIChannel<unknown, string>, "route"> & Omit<RunChannelParams<unknown>, "response" | "request"> & {
23
11
  userSessionService?: UserSessionService<any>;
24
12
  } & RunChannelOptions) => Promise<{
25
13
  openingData: unknown;
@@ -1,44 +1,13 @@
1
1
  import { NotFoundError } from '../errors/errors.js';
2
+ import { pikkuState } from '../pikku-state.js';
2
3
  import { coerceQueryStringToArray, validateSchema } from '../schema.js';
3
4
  import { match } from 'path-to-regexp';
4
- if (!globalThis.pikku?.channels) {
5
- globalThis.pikku = globalThis.pikku || {};
6
- globalThis.pikku.channels = [];
7
- globalThis.pikku.channelsMeta = [];
8
- }
9
- const channels = (data) => {
10
- if (data) {
11
- globalThis.pikku.channels = data;
12
- }
13
- return globalThis.pikku.channels;
14
- };
15
- const channelsMeta = (data) => {
16
- if (data) {
17
- globalThis.pikku.channelsMeta = data;
18
- }
19
- return globalThis.pikku.channelsMeta;
20
- };
21
- /**
22
- * Returns all the registered routes and associated metadata.
23
- * @internal
24
- */
25
- export const getChannels = () => {
26
- return {
27
- channels: channels(),
28
- channelsMeta: channelsMeta(),
29
- };
30
- };
31
5
  export const addChannel = (channel) => {
32
- channels().push(channel);
33
- };
34
- /**
35
- * @ignore
36
- */
37
- export const setChannelsMeta = (_channelsMeta) => {
38
- channelsMeta(_channelsMeta);
6
+ pikkuState('channel', 'channels').push(channel);
39
7
  };
40
8
  export const getMatchingChannelConfig = (request) => {
41
- const { channels, channelsMeta } = getChannels();
9
+ const channels = pikkuState('channel', 'channels');
10
+ const channelsMeta = pikkuState('channel', 'meta');
42
11
  for (const channelConfig of channels) {
43
12
  const cleanedRoute = channelConfig.route.replace(/^\/\//, '/');
44
13
  const cleanedRequest = request.replace(/^\/\//, '/');
@@ -58,7 +27,7 @@ export const getMatchingChannelConfig = (request) => {
58
27
  }
59
28
  return null;
60
29
  };
61
- export const openChannel = async ({ route, singletonServices, coerceToArray = false, http, userSessionService, }) => {
30
+ export const openChannel = async ({ route, singletonServices, coerceToArray = false, http, }) => {
62
31
  const matchingChannel = getMatchingChannelConfig(route);
63
32
  if (!matchingChannel) {
64
33
  singletonServices.logger.info(`Channel not found: ${route}`);
@@ -1,14 +1,17 @@
1
1
  import { CoreUserSession } from '../types/core.types.js';
2
- export type Channel<ChannelType = unknown, OpeningData = unknown, UserSession extends CoreUserSession = CoreUserSession> = {
2
+ export type Channel<ChannelType = unknown, OpeningData = unknown> = {
3
3
  channelId: string;
4
4
  channelName: string;
5
5
  channelObject?: ChannelType;
6
6
  openingData?: OpeningData;
7
- userSession?: UserSession;
8
7
  };
9
- export declare abstract class ChannelStore<ChannelType = unknown, OpeningData = unknown, UserSession extends CoreUserSession = CoreUserSession, TypedChannel = Channel<ChannelType, OpeningData, UserSession>> {
10
- abstract addChannel(channel: Channel<ChannelType, OpeningData, UserSession>): Promise<void> | void;
8
+ export declare abstract class ChannelStore<ChannelType = unknown, OpeningData = unknown, UserSession extends CoreUserSession = CoreUserSession, TypedChannel = Channel<ChannelType, OpeningData>> {
9
+ abstract addChannel(channel: Channel<ChannelType, OpeningData>): Promise<void> | void;
11
10
  abstract removeChannels(channelId: string[]): Promise<void> | void;
12
- abstract setUserSession(channelId: string, userSession: any): Promise<void> | void;
13
- abstract getChannel(channelId: string): Promise<TypedChannel> | TypedChannel;
11
+ abstract setUserSession(channelId: string, userSession: UserSession | null): Promise<void> | void;
12
+ abstract getChannelAndSession(channelId: string): Promise<TypedChannel & {
13
+ session: UserSession;
14
+ }> | (TypedChannel & {
15
+ session: UserSession;
16
+ });
14
17
  }
@@ -3,7 +3,6 @@ export * from './channel.types.js';
3
3
  export * from './log-channels.js';
4
4
  export * from './pikku-abstract-channel-handler.js';
5
5
  export * from './eventhub-service.js';
6
- export * from './eventhub-forwarder.js';
7
6
  export * from './eventhub-service.js';
8
7
  export * from './channel-store.js';
9
8
  export * from './eventhub-store.js';
@@ -3,7 +3,6 @@ export * from './channel.types.js';
3
3
  export * from './log-channels.js';
4
4
  export * from './pikku-abstract-channel-handler.js';
5
5
  export * from './eventhub-service.js';
6
- export * from './eventhub-forwarder.js';
7
6
  export * from './eventhub-service.js';
8
7
  export * from './channel-store.js';
9
8
  export * from './eventhub-store.js';
@@ -32,7 +32,7 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
32
32
  const allServices = {
33
33
  ...singletonServices,
34
34
  ...sessionServices,
35
- userSessionService,
35
+ userSession: userSessionService,
36
36
  };
37
37
  channelHandler.registerOnOpen(() => {
38
38
  channelConfig.onConnect?.(allServices, channel);
@@ -56,7 +56,7 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
56
56
  };
57
57
  await runMiddleware({
58
58
  ...singletonServices,
59
- userSessionService,
59
+ userSession: userSessionService,
60
60
  }, { http }, route.middleware || [], main);
61
61
  return channelHandler;
62
62
  };
@@ -1,10 +1,10 @@
1
- import { getChannels } from './channel-runner.js';
1
+ import { pikkuState } from '../pikku-state.js';
2
2
  /**
3
3
  * Logs all the loaded channels.
4
4
  * @param logger - A logger for logging information.
5
5
  */
6
6
  export const logChannels = (logger) => {
7
- const { channels } = getChannels();
7
+ const channels = pikkuState('channel', 'channels');
8
8
  if (channels.length === 0) {
9
9
  logger.info('No channels added');
10
10
  return;
@@ -1,12 +1,13 @@
1
1
  import { closeSessionServices } from '../../utils.js';
2
2
  import { processMessageHandlers } from '../channel-handler.js';
3
- import { getChannels, openChannel } from '../channel-runner.js';
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
6
  import { RemoteUserSessionService } from '../../services/user-session-service.js';
7
7
  import { runMiddleware } from '../../middleware-runner.js';
8
+ import { pikkuState } from '../../pikku-state.js';
8
9
  const getVariablesForChannel = ({ channelId, channelName, channelHandlerFactory, openingData, }) => {
9
- const { channels } = getChannels();
10
+ const channels = pikkuState('channel', 'channels');
10
11
  const channelConfig = channels.find((channelConfig) => channelConfig.name === channelName);
11
12
  if (!channelConfig) {
12
13
  throw new Error(`Channel not found: ${channelName}`);
@@ -60,19 +61,19 @@ export const runChannelConnect = async ({ singletonServices, channelId, channelO
60
61
  };
61
62
  await runMiddleware({
62
63
  ...singletonServices,
63
- userSessionService,
64
+ userSession: userSessionService,
64
65
  }, { http }, channelConfig.middleware || [], main);
65
66
  };
66
67
  export const runChannelDisconnect = async ({ singletonServices, ...params }) => {
67
68
  let sessionServices;
68
- const { userSession, openingData, channelName } = await params.channelStore.getChannel(params.channelId);
69
+ const { openingData, channelName, session } = await params.channelStore.getChannelAndSession(params.channelId);
69
70
  const { channel, channelConfig } = getVariablesForChannel({
70
71
  ...params,
71
72
  openingData,
72
73
  channelName,
73
74
  });
74
75
  if (!sessionServices && params.createSessionServices) {
75
- sessionServices = await params.createSessionServices(singletonServices, {}, userSession);
76
+ sessionServices = await params.createSessionServices(singletonServices, {}, session);
76
77
  }
77
78
  await channelConfig.onDisconnect?.({ ...singletonServices, ...sessionServices }, channel);
78
79
  await params.channelStore.removeChannels([channel.channelId]);
@@ -82,14 +83,14 @@ export const runChannelDisconnect = async ({ singletonServices, ...params }) =>
82
83
  };
83
84
  export const runChannelMessage = async ({ singletonServices, ...params }, data) => {
84
85
  let sessionServices;
85
- const { userSession, openingData, channelName } = await params.channelStore.getChannel(params.channelId);
86
+ const { openingData, channelName, session } = await params.channelStore.getChannelAndSession(params.channelId);
86
87
  const { channelHandler, channelConfig } = getVariablesForChannel({
87
88
  ...params,
88
89
  openingData,
89
90
  channelName,
90
91
  });
91
92
  if (params.createSessionServices) {
92
- sessionServices = await params.createSessionServices(singletonServices, {}, userSession);
93
+ sessionServices = await params.createSessionServices(singletonServices, {}, session);
93
94
  }
94
95
  let response;
95
96
  try {
@@ -16,7 +16,7 @@ export interface ErrorDetails {
16
16
  status: number;
17
17
  message: string;
18
18
  }
19
- export declare const getErrors: () => Map<any, ErrorDetails>;
19
+ export declare const getErrors: () => Map<PikkuError, ErrorDetails>;
20
20
  /**
21
21
  * Adds an error to the API errors map.
22
22
  * @param error - The error to add.
@@ -1,3 +1,4 @@
1
+ import { pikkuState } from '../pikku-state.js';
1
2
  /**
2
3
  * Base class for custom errors.
3
4
  * @extends {Error}
@@ -12,24 +13,14 @@ export class PikkuError extends Error {
12
13
  Object.setPrototypeOf(this, new.target.prototype);
13
14
  }
14
15
  }
15
- /**
16
- * Map of API errors to their details.
17
- */
18
- if (!globalThis.pikku?.apiErrors) {
19
- globalThis.pikku = globalThis.pikku || {};
20
- globalThis.pikku.apiErrors = new Map([]);
21
- }
22
- const apiErrors = () => {
23
- return globalThis.pikku.apiErrors;
24
- };
25
- export const getErrors = () => apiErrors();
16
+ export const getErrors = () => pikkuState('errors', 'errors');
26
17
  /**
27
18
  * Adds an error to the API errors map.
28
19
  * @param error - The error to add.
29
20
  * @param details - The details of the error.
30
21
  */
31
22
  export const addError = (error, { status, message }) => {
32
- apiErrors().set(error, { status, message });
23
+ pikkuState('errors', 'errors').set(error, { status, message });
33
24
  };
34
25
  /**
35
26
  * Adds multiple errors to the API errors map.
@@ -46,9 +37,10 @@ export const addErrors = (errors) => {
46
37
  * @returns An object containing the status and message, or undefined if the error is not found.
47
38
  */
48
39
  export const getErrorResponse = (error) => {
49
- const foundError = Array.from(apiErrors().entries()).find(([e]) => e.name === error.constructor.name);
40
+ const errors = Array.from(pikkuState('errors', 'errors').entries());
41
+ const foundError = errors.find(([e]) => e.name === error.constructor.name);
50
42
  if (foundError) {
51
43
  return foundError[1];
52
44
  }
53
- return apiErrors().get(error);
45
+ return pikkuState('errors', 'errors').get(error);
54
46
  };
@@ -69,7 +69,7 @@ export declare class NotAcceptableError extends PikkuError {
69
69
  export declare class ProxyAuthenticationRequiredError extends PikkuError {
70
70
  }
71
71
  /**
72
- * The server did not receive a timely response from an upstream server.
72
+ * The server request expired
73
73
  * @group Error
74
74
  */
75
75
  export declare class RequestTimeoutError extends PikkuError {
@@ -114,14 +114,14 @@ addError(ProxyAuthenticationRequiredError, {
114
114
  message: 'The client must authenticate itself to get the requested response.',
115
115
  });
116
116
  /**
117
- * The server did not receive a timely response from an upstream server.
117
+ * The server request expired
118
118
  * @group Error
119
119
  */
120
120
  export class RequestTimeoutError extends PikkuError {
121
121
  }
122
122
  addError(RequestTimeoutError, {
123
123
  status: 408,
124
- message: 'The server did not receive a timely response from an upstream server.',
124
+ message: 'The request timeout has expired.',
125
125
  });
126
126
  /**
127
127
  * The request could not be completed due to a conflict with the current state of the target resource.
@@ -1,4 +1,5 @@
1
- import { CoreHTTPFunctionRoute, HTTPRoutesMeta, RunRouteOptions, RunRouteParams, PikkuHTTP } from './http-routes.types.js';
1
+ import { CoreHTTPFunctionRoute, RunRouteOptions, RunRouteParams, PikkuHTTP } from './http-routes.types.js';
2
+ import { PikkuMiddleware } from '../types/core.types.js';
2
3
  import { PikkuRequest } from '../pikku-request.js';
3
4
  import { PikkuResponse } from '../pikku-response.js';
4
5
  /**
@@ -7,34 +8,13 @@ import { PikkuResponse } from '../pikku-response.js';
7
8
  * @param {APIMiddleware[] | string} routeOrMiddleware - Route pattern to match or middleware array
8
9
  * @param {APIMiddleware} [middleware] - Middleware to add (required if first param is a string)
9
10
  */
10
- export declare const addMiddleware: <APIMiddleware>(routeOrMiddleware: APIMiddleware[] | string, middleware?: APIMiddleware) => void;
11
+ export declare const addMiddleware: <APIMiddleware extends PikkuMiddleware>(routeOrMiddleware: APIMiddleware[] | string, middleware?: APIMiddleware[]) => void;
11
12
  /**
12
13
  * Add a route to the global routes registry
13
14
  *
14
15
  * @param {CoreHTTPFunctionRoute} route - Route configuration to add
15
16
  */
16
17
  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
- */
20
- export declare const clearRoutes: () => void;
21
- /**
22
- * Set the HTTP routes metadata
23
- *
24
- * @param {HTTPRoutesMeta} routeMeta - Metadata for routes
25
- * @ignore
26
- */
27
- export declare const setHTTPRoutesMeta: (routeMeta: HTTPRoutesMeta) => void;
28
- /**
29
- * Returns all the registered routes and associated metadata
30
- *
31
- * @returns {Object} Object containing routes and routesMeta
32
- * @internal
33
- */
34
- export declare const getRoutes: () => {
35
- routes: CoreHTTPFunctionRoute<any, any, any>[];
36
- routesMeta: HTTPRoutesMeta;
37
- };
38
18
  /**
39
19
  * Create an HTTP interaction object from request and response
40
20
  *
@@ -9,51 +9,7 @@ import { coerceQueryStringToArray, validateSchema } from '../schema.js';
9
9
  import { LocalUserSessionService } from '../services/user-session-service.js';
10
10
  import { runMiddleware } from '../middleware-runner.js';
11
11
  import { handleError } from '../handle-error.js';
12
- /**
13
- * Initialize global state for HTTP routes and middleware if not already available
14
- */
15
- if (!globalThis.pikku?.httpRoutes) {
16
- globalThis.pikku = globalThis.pikku || {};
17
- globalThis.pikku.httpMiddleware = [];
18
- globalThis.pikku.httpRoutes = [];
19
- globalThis.pikku.httpRoutesMeta = [];
20
- }
21
- /**
22
- * Get or set the global HTTP routes
23
- *
24
- * @param {CoreHTTPFunctionRoute<any, any, any>[]} [data] - Optional routes data to set
25
- * @returns {CoreHTTPFunctionRoute<any, any, any>[]} Current routes
26
- */
27
- const httpRoutes = (data) => {
28
- if (data) {
29
- globalThis.pikku.httpRoutes = data;
30
- }
31
- return globalThis.pikku.httpRoutes;
32
- };
33
- /**
34
- * Get or set the global HTTP route metadata
35
- *
36
- * @param {HTTPRoutesMeta} [data] - Optional route metadata to set
37
- * @returns {HTTPRoutesMeta} Current route metadata
38
- */
39
- const httpRoutesMeta = (data) => {
40
- if (data) {
41
- globalThis.pikku.httpRoutesMeta = data;
42
- }
43
- return globalThis.pikku.httpRoutesMeta;
44
- };
45
- /**
46
- * Get or set the global HTTP middleware
47
- *
48
- * @param {HTTPRouteMiddleware[]} [data] - Optional middleware to set
49
- * @returns {HTTPRouteMiddleware[]} Current middleware
50
- */
51
- const httpMiddleware = (data) => {
52
- if (data) {
53
- globalThis.pikku.httpMiddleware = data;
54
- }
55
- return globalThis.pikku.httpMiddleware;
56
- };
12
+ import { pikkuState } from '../pikku-state.js';
57
13
  /**
58
14
  * Add middleware to a specific route or globally
59
15
  *
@@ -61,14 +17,14 @@ const httpMiddleware = (data) => {
61
17
  * @param {APIMiddleware} [middleware] - Middleware to add (required if first param is a string)
62
18
  */
63
19
  export const addMiddleware = (routeOrMiddleware, middleware) => {
64
- if (typeof routeOrMiddleware === 'string') {
65
- globalThis.pikku.httpMiddleware.push({
20
+ if (typeof routeOrMiddleware === 'string' && middleware) {
21
+ pikkuState('http', 'middleware').push({
66
22
  route: routeOrMiddleware,
67
- middleware: middleware,
23
+ middleware,
68
24
  });
69
25
  }
70
26
  else {
71
- globalThis.pikku.httpMiddleware.push({
27
+ pikkuState('http', 'middleware').push({
72
28
  route: '*',
73
29
  middleware: routeOrMiddleware,
74
30
  });
@@ -80,34 +36,7 @@ export const addMiddleware = (routeOrMiddleware, middleware) => {
80
36
  * @param {CoreHTTPFunctionRoute} route - Route configuration to add
81
37
  */
82
38
  export const addRoute = (route) => {
83
- httpRoutes().push(route);
84
- };
85
- /**
86
- * Remove all routes from the global registry
87
- */
88
- export const clearRoutes = () => {
89
- httpRoutes([]);
90
- };
91
- /**
92
- * Set the HTTP routes metadata
93
- *
94
- * @param {HTTPRoutesMeta} routeMeta - Metadata for routes
95
- * @ignore
96
- */
97
- export const setHTTPRoutesMeta = (routeMeta) => {
98
- httpRoutesMeta(routeMeta);
99
- };
100
- /**
101
- * Returns all the registered routes and associated metadata
102
- *
103
- * @returns {Object} Object containing routes and routesMeta
104
- * @internal
105
- */
106
- export const getRoutes = () => {
107
- return {
108
- routes: httpRoutes(),
109
- routesMeta: httpRoutesMeta(),
110
- };
39
+ pikkuState('http', 'routes').push(route);
111
40
  };
112
41
  /**
113
42
  * Find a route that matches the given request type and path
@@ -117,7 +46,10 @@ export const getRoutes = () => {
117
46
  * @returns {Object | undefined} Matching route information or undefined if no match
118
47
  */
119
48
  const getMatchingRoute = (requestType, requestPath) => {
120
- for (const route of httpRoutes()) {
49
+ const routes = pikkuState('http', 'routes');
50
+ const middleware = pikkuState('http', 'middleware');
51
+ const routesMeta = pikkuState('http', 'meta');
52
+ for (const route of routes) {
121
53
  // Skip routes that don't match the request method
122
54
  if (route.method !== requestType.toLowerCase()) {
123
55
  continue;
@@ -130,12 +62,12 @@ const getMatchingRoute = (requestType, requestPath) => {
130
62
  const matchedPath = matchFunc(requestPath.replace(/^\/\//, '/'));
131
63
  if (matchedPath) {
132
64
  // Get all middleware for this route
133
- const globalMiddleware = httpMiddleware()
65
+ const globalMiddleware = middleware
134
66
  .filter((m) => m.route === '*' || new RegExp(m.route).test(route.route))
135
67
  .map((m) => m.middleware)
136
68
  .flat();
137
69
  // Find schema for this route
138
- const schemaName = httpRoutesMeta().find((routeMeta) => routeMeta.method === route.method && routeMeta.route === route.route)?.input;
70
+ const schemaName = routesMeta.find((routeMeta) => routeMeta.method === route.method && routeMeta.route === route.route)?.input;
139
71
  return {
140
72
  matchedPath,
141
73
  params: matchedPath.params,
@@ -180,7 +112,6 @@ export const createHTTPInteraction = (request, response) => {
180
112
  const executeRouteWithMiddleware = async (services, matchedRoute, http, options) => {
181
113
  const { matchedPath, params, route, middleware, schemaName } = matchedRoute;
182
114
  const { singletonServices, userSessionService, createSessionServices, skipUserSession, } = services;
183
- const { coerceToArray } = options;
184
115
  const requiresSession = route.auth !== false;
185
116
  let sessionServices;
186
117
  let result;
@@ -206,7 +137,7 @@ const executeRouteWithMiddleware = async (services, matchedRoute, http, options)
206
137
  const data = await http?.request?.getData();
207
138
  // Validate schema
208
139
  validateSchema(singletonServices.logger, singletonServices.schemaService, schemaName, data);
209
- if (coerceToArray && schemaName) {
140
+ if (options.coerceToArray && schemaName) {
210
141
  coerceQueryStringToArray(schemaName, data);
211
142
  }
212
143
  // Run permission checks
@@ -1,5 +1,5 @@
1
1
  export * from './pikku-http-abstract-request.js';
2
2
  export * from './pikku-http-abstract-response.js';
3
3
  export * from './log-http-routes.js';
4
- export { runHTTPRoute, getRoutes, setHTTPRoutesMeta, addRoute, } from './http-route-runner.js';
4
+ export { runHTTPRoute, addRoute } from './http-route-runner.js';
5
5
  export type * from './http-routes.types.js';
@@ -1,4 +1,4 @@
1
1
  export * from './pikku-http-abstract-request.js';
2
2
  export * from './pikku-http-abstract-response.js';
3
3
  export * from './log-http-routes.js';
4
- export { runHTTPRoute, getRoutes, setHTTPRoutesMeta, addRoute, } from './http-route-runner.js';
4
+ export { runHTTPRoute, addRoute } from './http-route-runner.js';
@@ -1,10 +1,10 @@
1
- import { getRoutes } from './http-route-runner.js';
1
+ import { pikkuState } from '../pikku-state.js';
2
2
  /**
3
3
  * Logs all the loaded routes.
4
4
  * @param logger - A logger for logging information.
5
5
  */
6
6
  export const logRoutes = (logger) => {
7
- const { routes } = getRoutes();
7
+ const routes = pikkuState('http', 'routes');
8
8
  if (routes.length === 0) {
9
9
  logger.info('No routes added');
10
10
  return;
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@ export * from './channel/index.js';
11
11
  export * from './scheduler/index.js';
12
12
  export * from './errors/index.js';
13
13
  export * from './middleware/index.js';
14
+ export { pikkuState } from './pikku-state.js';
14
15
  export { runMiddleware } from './middleware-runner.js';
15
16
  export { addRoute } from './http/http-route-runner.js';
16
17
  export { addChannel } from './channel/channel-runner.js';
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ export * from './channel/index.js';
11
11
  export * from './scheduler/index.js';
12
12
  export * from './errors/index.js';
13
13
  export * from './middleware/index.js';
14
+ export { pikkuState } from './pikku-state.js';
14
15
  export { runMiddleware } from './middleware-runner.js';
15
16
  export { addRoute } from './http/http-route-runner.js';
16
17
  export { addChannel } from './channel/channel-runner.js';
@@ -0,0 +1,15 @@
1
+ import { CoreConfig, CoreSingletonServices, CoreUserSession, PikkuMiddleware } from '../types/core.types.js';
2
+ /**
3
+ * API key middleware that retrieves a session from the 'x-api-key' header using a provided callback.
4
+ *
5
+ * @param options.getSessionForAPIKey - A function that returns a session when provided an API key.
6
+ */
7
+ export declare const authAPIKey: <SingletonServices extends CoreSingletonServices<CoreConfig>, UserSession extends CoreUserSession>({ source, getSessionForAPIKey, jwt, }: {
8
+ source: "header" | "query" | "all";
9
+ } & ({
10
+ getSessionForAPIKey?: undefined;
11
+ jwt?: true;
12
+ } | {
13
+ getSessionForAPIKey: (services: SingletonServices, apiKey: string) => Promise<any>;
14
+ jwt?: false;
15
+ })) => PikkuMiddleware;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * API key middleware that retrieves a session from the 'x-api-key' header using a provided callback.
3
+ *
4
+ * @param options.getSessionForAPIKey - A function that returns a session when provided an API key.
5
+ */
6
+ export const authAPIKey = ({ source, getSessionForAPIKey, jwt, }) => {
7
+ const middleware = async (services, { http }, next) => {
8
+ if (!http?.request || services.userSession.get()) {
9
+ return next();
10
+ }
11
+ let apiKey = null;
12
+ if (source === 'header' || source === 'all') {
13
+ apiKey = http.request.getHeader('x-api-key');
14
+ }
15
+ if (!apiKey && (source === 'query' || source === 'all')) {
16
+ apiKey = http.request.getQuery().apiKey;
17
+ }
18
+ if (apiKey) {
19
+ let userSession = null;
20
+ if (jwt) {
21
+ if (!services.jwt) {
22
+ throw new Error('JWT service is required for JWT decoding.');
23
+ }
24
+ userSession = await services.jwt.decode(apiKey);
25
+ }
26
+ else {
27
+ userSession = await getSessionForAPIKey(services, apiKey);
28
+ }
29
+ if (userSession) {
30
+ await services.userSession.set(userSession);
31
+ }
32
+ }
33
+ return next();
34
+ };
35
+ return middleware;
36
+ };
@@ -0,0 +1,12 @@
1
+ import { CoreConfig, CoreSingletonServices, CoreUserSession, PikkuMiddleware } from '../types/core.types.js';
2
+ /**
3
+ * Extracts the Bearer token from the Authorization header
4
+ */
5
+ export declare const authBearer: <SingletonServices extends CoreSingletonServices<CoreConfig>, UserSession extends CoreUserSession>({ token, jwt, getSession, }?: {
6
+ token?: {
7
+ value: string;
8
+ userSession: UserSession;
9
+ };
10
+ jwt?: boolean;
11
+ getSession?: (services: SingletonServices, token: string) => Promise<UserSession> | UserSession;
12
+ }) => PikkuMiddleware;