@pikku/core 0.6.13 → 0.6.14

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 (79) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/channel/channel-handler.d.ts +2 -2
  3. package/dist/channel/channel-handler.js +4 -4
  4. package/dist/channel/channel-runner.d.ts +5 -4
  5. package/dist/channel/channel-runner.js +8 -14
  6. package/dist/channel/channel.types.d.ts +11 -18
  7. package/dist/channel/local/local-channel-handler.d.ts +1 -3
  8. package/dist/channel/local/local-channel-handler.js +0 -3
  9. package/dist/channel/local/local-channel-runner.js +49 -33
  10. package/dist/channel/pikku-abstract-channel-handler.d.ts +4 -7
  11. package/dist/channel/pikku-abstract-channel-handler.js +1 -5
  12. package/dist/channel/serverless/serverless-channel-runner.js +45 -38
  13. package/dist/handle-error.d.ts +14 -0
  14. package/dist/handle-error.js +53 -0
  15. package/dist/http/http-route-runner.d.ts +34 -11
  16. package/dist/http/http-route-runner.js +177 -106
  17. package/dist/http/http-routes.types.d.ts +15 -9
  18. package/dist/http/index.d.ts +0 -1
  19. package/dist/http/index.js +0 -1
  20. package/dist/http/pikku-http-abstract-request.d.ts +4 -1
  21. package/dist/http/pikku-http-abstract-request.js +7 -0
  22. package/dist/index.d.ts +1 -0
  23. package/dist/index.js +1 -0
  24. package/dist/middleware/auth-api-key.d.ts +9 -0
  25. package/dist/middleware/auth-api-key.js +23 -0
  26. package/dist/middleware/auth-cookie-middleware.d.ts +11 -0
  27. package/dist/middleware/auth-cookie-middleware.js +36 -0
  28. package/dist/middleware/auth-jwt-middleware.d.ts +10 -0
  29. package/dist/middleware/auth-jwt-middleware.js +32 -0
  30. package/dist/middleware/auth-query-middleware.d.ts +10 -0
  31. package/dist/middleware/auth-query-middleware.js +21 -0
  32. package/dist/middleware/index.d.ts +4 -0
  33. package/dist/middleware/index.js +4 -0
  34. package/dist/middleware-runner.d.ts +22 -0
  35. package/dist/middleware-runner.js +28 -0
  36. package/dist/scheduler/scheduler.types.d.ts +2 -0
  37. package/dist/schema.d.ts +1 -1
  38. package/dist/schema.js +3 -6
  39. package/dist/services/user-session-service.d.ts +20 -3
  40. package/dist/services/user-session-service.js +35 -1
  41. package/dist/types/core.types.d.ts +11 -15
  42. package/dist/types/functions.types.d.ts +4 -4
  43. package/lcov.info +1574 -1731
  44. package/package.json +1 -2
  45. package/src/channel/channel-handler.ts +6 -11
  46. package/src/channel/channel-runner.ts +13 -32
  47. package/src/channel/channel.types.ts +13 -39
  48. package/src/channel/local/local-channel-handler.ts +1 -7
  49. package/src/channel/local/local-channel-runner.test.ts +2 -5
  50. package/src/channel/local/local-channel-runner.ts +72 -54
  51. package/src/channel/pikku-abstract-channel-handler.test.ts +3 -28
  52. package/src/channel/pikku-abstract-channel-handler.ts +3 -9
  53. package/src/channel/serverless/serverless-channel-runner.ts +68 -56
  54. package/src/handle-error.ts +67 -0
  55. package/src/http/http-route-runner.test.ts +12 -45
  56. package/src/http/http-route-runner.ts +256 -182
  57. package/src/http/http-routes.types.ts +15 -10
  58. package/src/http/index.ts +0 -2
  59. package/src/http/pikku-http-abstract-request.ts +8 -1
  60. package/src/index.ts +1 -0
  61. package/src/middleware/auth-api-key.ts +32 -0
  62. package/src/middleware/auth-cookie-middleware.ts +54 -0
  63. package/src/middleware/auth-jwt-middleware.ts +47 -0
  64. package/src/middleware/auth-query-middleware.ts +33 -0
  65. package/src/middleware/index.ts +4 -0
  66. package/src/middleware-runner.ts +43 -0
  67. package/src/scheduler/scheduler.types.ts +2 -0
  68. package/src/schema.ts +4 -8
  69. package/src/services/user-session-service.ts +45 -3
  70. package/src/types/core.types.ts +19 -18
  71. package/src/types/functions.types.ts +11 -7
  72. package/tsconfig.tsbuildinfo +1 -1
  73. package/dist/http/http-session-service.d.ts +0 -15
  74. package/dist/http/http-session-service.js +0 -1
  75. package/dist/http/pikku-http-session-service.d.ts +0 -45
  76. package/dist/http/pikku-http-session-service.js +0 -92
  77. package/src/http/http-session-service.ts +0 -19
  78. package/src/http/pikku-http-session-service.test.ts +0 -106
  79. package/src/http/pikku-http-session-service.ts +0 -135
@@ -0,0 +1,32 @@
1
+ import { PikkuMiddleware } from '../types/core.types.js'
2
+
3
+ /**
4
+ * API key middleware that retrieves a session from the 'x-api-key' header using a provided callback.
5
+ *
6
+ * @param options.getSessionForAPIKey - A function that returns a session when provided an API key.
7
+ */
8
+ export const apiKeyMiddleware = (
9
+ options: { getSessionForAPIKey?: (apiKey: string) => Promise<any> } = {}
10
+ ) => {
11
+ const middleware: PikkuMiddleware = async (
12
+ { userSessionService },
13
+ { http },
14
+ next
15
+ ) => {
16
+ if (!http?.request || userSessionService.get()) {
17
+ return next()
18
+ }
19
+
20
+ if (options.getSessionForAPIKey) {
21
+ const apiKey = http.request.getHeader('x-api-key')
22
+ if (apiKey) {
23
+ const userSession = await options.getSessionForAPIKey(apiKey)
24
+ if (userSession) {
25
+ await userSessionService.set(userSession)
26
+ }
27
+ }
28
+ }
29
+ return next()
30
+ }
31
+ return middleware
32
+ }
@@ -0,0 +1,54 @@
1
+ import { PikkuMiddleware } from '../types/core.types.js'
2
+
3
+ /**
4
+ * Cookie middleware that extracts a session from cookies.
5
+ *
6
+ * @param options.cookieNames - List of cookie names to check.
7
+ * @param options.getSessionForCookieValue - Function to retrieve a session using a cookie value.
8
+ */
9
+ export const cookieMiddleware = (
10
+ options: {
11
+ cookieNames?: string[]
12
+ getSessionForCookieValue?: (
13
+ cookieValue: string,
14
+ cookieName: string
15
+ ) => Promise<any>
16
+ } = {}
17
+ ): PikkuMiddleware => {
18
+ const middleware: PikkuMiddleware = async (
19
+ { userSessionService },
20
+ { http },
21
+ next
22
+ ) => {
23
+ if (!http?.request || userSessionService.get()) {
24
+ return next()
25
+ }
26
+
27
+ if (options.getSessionForCookieValue && options.cookieNames) {
28
+ const cookies = http.request.getCookies()
29
+ if (cookies) {
30
+ let cookieName: string | undefined
31
+ for (const name of options.cookieNames) {
32
+ if (cookies[name]) {
33
+ cookieName = name
34
+ break
35
+ }
36
+ }
37
+ if (cookieName) {
38
+ const cookieValue = cookies[cookieName]
39
+ if (cookieValue) {
40
+ const userSession = await options.getSessionForCookieValue(
41
+ cookieValue,
42
+ cookieName
43
+ )
44
+ if (userSession) {
45
+ await userSessionService.set(userSession)
46
+ }
47
+ }
48
+ }
49
+ }
50
+ }
51
+ return next()
52
+ }
53
+ return middleware
54
+ }
@@ -0,0 +1,47 @@
1
+ import { InvalidSessionError } from '../errors/errors.js'
2
+ import { PikkuMiddleware } from '../types/core.types.js'
3
+
4
+ /**
5
+ * JWT middleware that extracts the Bearer token from the Authorization header,
6
+ * decodes it via the jwtService, and sets the session if found.
7
+ *
8
+ * @param options.debugJWTDecode - Optional flag for debugging the JWT decode process.
9
+ */
10
+ export const jwtMiddleware = (
11
+ options: { debugJWTDecode?: boolean } = {}
12
+ ): PikkuMiddleware => {
13
+ const middleware: PikkuMiddleware = async (
14
+ { jwt, userSessionService },
15
+ { http },
16
+ next
17
+ ) => {
18
+ if (!jwt) {
19
+ throw new Error('JWT service not found')
20
+ }
21
+
22
+ // Skip if session already exists.
23
+ if (!http?.request || userSessionService.get()) {
24
+ return next()
25
+ }
26
+
27
+ const authHeader =
28
+ http.request.getHeader('authorization') ||
29
+ http.request.getHeader('Authorization')
30
+ if (authHeader) {
31
+ const [scheme, token] = authHeader.split(' ')
32
+ if (scheme !== 'Bearer' || !token) {
33
+ throw new InvalidSessionError()
34
+ }
35
+ const userSession = await jwt.decode<any>(
36
+ token,
37
+ undefined,
38
+ options.debugJWTDecode
39
+ )
40
+ if (userSession) {
41
+ await userSessionService.set(userSession)
42
+ }
43
+ }
44
+ return next()
45
+ }
46
+ return middleware
47
+ }
@@ -0,0 +1,33 @@
1
+ import { PikkuQuery } from '../http/http-routes.types.js'
2
+ import { PikkuMiddleware } from '../types/core.types.js'
3
+
4
+ /**
5
+ * Query middleware that retrieves a session using query parameters.
6
+ *
7
+ * @param options.getSessionForQueryValue - A function that returns a session based on query parameters.
8
+ */
9
+ export const queryMiddleware = (
10
+ options: {
11
+ getSessionForQueryValue?: (query: PikkuQuery) => Promise<any>
12
+ } = {}
13
+ ): PikkuMiddleware => {
14
+ const middleware: PikkuMiddleware = async (
15
+ { userSessionService },
16
+ { http },
17
+ next
18
+ ) => {
19
+ if (!http?.request || userSessionService.get()) {
20
+ return next()
21
+ }
22
+
23
+ if (options.getSessionForQueryValue) {
24
+ const query = http.request.getQuery()
25
+ const userSession = await options.getSessionForQueryValue(query)
26
+ if (userSession) {
27
+ await userSessionService.set(userSession)
28
+ }
29
+ }
30
+ return next()
31
+ }
32
+ return middleware
33
+ }
@@ -0,0 +1,4 @@
1
+ export * from './auth-api-key.js'
2
+ export * from './auth-cookie-middleware.js'
3
+ export * from './auth-jwt-middleware.js'
4
+ export * from './auth-query-middleware.js'
@@ -0,0 +1,43 @@
1
+ import { UserSessionService } from './services/user-session-service.js'
2
+ import {
3
+ CoreSingletonServices,
4
+ PikkuInteraction,
5
+ PikkuMiddleware,
6
+ } from './types/core.types.js'
7
+
8
+ /**
9
+ * Runs a chain of middleware functions in sequence before executing the main function.
10
+ *
11
+ * @param services - An object containing services (e.g., singletonServices, userSessionService, etc.)
12
+ * @param interaction - The interaction context, e.g., { http }.
13
+ * @param middlewares - An array of middleware functions to run.
14
+ * @param main - The main function to execute after all middleware have run.
15
+ * @returns A promise resolving to the result of the main function.
16
+ *
17
+ * @example
18
+ * runMiddleware(
19
+ * { ...services, userSessionService },
20
+ * { http },
21
+ * [middleware1, middleware2, middleware3],
22
+ * async () => { return await runMain(); }
23
+ * );
24
+ */
25
+ export const runMiddleware = async (
26
+ services: CoreSingletonServices & {
27
+ userSessionService: UserSessionService<any>
28
+ },
29
+ interaction: PikkuInteraction,
30
+ middlewares: PikkuMiddleware[],
31
+ main?: () => Promise<void>
32
+ ): Promise<void> => {
33
+ const dispatch = async (index: number): Promise<any> => {
34
+ if (middlewares && index < middlewares.length) {
35
+ await middlewares[index]!(services, interaction, () =>
36
+ dispatch(index + 1)
37
+ )
38
+ } else if (main) {
39
+ return await main()
40
+ }
41
+ }
42
+ await dispatch(0)
43
+ }
@@ -10,6 +10,7 @@ export type ScheduledTasksMeta<UserSession extends CoreUserSession = any> =
10
10
  schedule: string
11
11
  session?: UserSession
12
12
  docs?: APIDocs
13
+ tags?: string[]
13
14
  }>
14
15
 
15
16
  /**
@@ -24,4 +25,5 @@ export type CoreScheduledTask<
24
25
  func: APIFunction
25
26
  docs?: APIDocs
26
27
  session?: UserSession
28
+ tags?: string[]
27
29
  }
package/src/schema.ts CHANGED
@@ -98,12 +98,11 @@ export const coerceQueryStringToArray = (schemaName: string, data: any) => {
98
98
  }
99
99
  }
100
100
 
101
- export const validateAndCoerce = (
101
+ export const validateSchema = async (
102
102
  logger: Logger,
103
103
  schemaService: SchemaService | undefined,
104
104
  schemaName: string | undefined | null,
105
- data: any,
106
- coerceToArray: boolean
105
+ data: any
107
106
  ) => {
108
107
  if (schemaService) {
109
108
  if (!schemaName) {
@@ -115,10 +114,7 @@ export const validateAndCoerce = (
115
114
  }
116
115
  }
117
116
  const schema = getSchema(schemaName)
118
- schemaService.compileSchema(schemaName, schema)
119
- if (coerceToArray) {
120
- coerceQueryStringToArray(schemaName, data)
121
- }
122
- schemaService.validateSchema(schemaName, data)
117
+ await schemaService.compileSchema(schemaName, schema)
118
+ await schemaService.validateSchema(schemaName, data)
123
119
  }
124
120
  }
@@ -1,7 +1,49 @@
1
+ import { ChannelStore } from '../channel/channel-store.js'
1
2
  import { CoreUserSession } from '../types/core.types.js'
2
3
 
3
4
  export interface UserSessionService<UserSession extends CoreUserSession> {
4
- setSession: (session: UserSession) => Promise<void> | void
5
- deleteSession: () => Promise<void> | void
6
- getSession: () => Promise<UserSession | void> | UserSession | void
5
+ set(session: UserSession): Promise<void> | void
6
+ clear(): Promise<void> | void
7
+ get(): Promise<UserSession | undefined> | UserSession | undefined
8
+ }
9
+
10
+ export class LocalUserSessionService<UserSession extends CoreUserSession>
11
+ implements UserSessionService<UserSession>
12
+ {
13
+ constructor(private session: UserSession | undefined = undefined) {}
14
+
15
+ public set(session: UserSession) {
16
+ this.session = session
17
+ }
18
+
19
+ public clear() {
20
+ this.session = undefined
21
+ }
22
+
23
+ public get(): UserSession | undefined {
24
+ return this.session
25
+ }
26
+ }
27
+
28
+ export class RemoteUserSessionService<UserSession extends CoreUserSession>
29
+ implements UserSessionService<UserSession>
30
+ {
31
+ constructor(
32
+ private channelStore: ChannelStore<unknown, unknown, UserSession>,
33
+ private channelId: string,
34
+ public session: UserSession | undefined = undefined
35
+ ) {}
36
+
37
+ public async set(session: UserSession) {
38
+ await this.channelStore.setUserSession(this.channelId, session)
39
+ }
40
+
41
+ public async clear() {
42
+ await this.channelStore.setUserSession(this.channelId, undefined)
43
+ }
44
+
45
+ public async get(): Promise<UserSession | undefined> {
46
+ const channel = await this.channelStore.getChannel(this.channelId)
47
+ return channel.userSession
48
+ }
7
49
  }
@@ -1,10 +1,8 @@
1
1
  import type { Logger, LogLevel } from '../services/logger.js'
2
- import type { HTTPSessionService } from '../http/http-session-service.js'
3
2
  import { VariablesService } from '../services/variables-service.js'
4
3
  import { EventHubService } from '../channel/eventhub-service.js'
5
4
  import { SchemaService } from '../services/schema-service.js'
6
- import { enforceChannelAccess } from '../channel/channel.types.js'
7
- import { enforceHTTPAccess, PikkuHTTP } from '../http/http-routes.types.js'
5
+ import { PikkuHTTP } from '../http/http-routes.types.js'
8
6
  import { UserSessionService } from '../services/user-session-service.js'
9
7
  import { JWTService } from '../services/jwt-service.js'
10
8
  import { SecretService } from '../services/secret-service.js'
@@ -51,8 +49,6 @@ export type RequireAtLeastOne<T> = {
51
49
  export type CoreConfig<Config extends Record<string, unknown> = {}> = {
52
50
  /** The log level for the application. */
53
51
  logLevel?: LogLevel
54
- /** The maximum compute time allowed, in milliseconds (optional). */
55
- maximumComputeTime?: number
56
52
  /** Secrets used by the application (optional). */
57
53
  secrets?: {}
58
54
  } & Config
@@ -69,14 +65,8 @@ export interface CoreSingletonServices<
69
65
  Config extends CoreConfig = CoreConfig,
70
66
  UserSession extends CoreUserSession = CoreUserSession,
71
67
  > {
72
- /** The http permission service used for authorization (optional). */
73
- enforceHTTPAccess?: enforceHTTPAccess
74
- /** The channel permission service used by the application (optional). */
75
- enforceChannelAccess?: enforceChannelAccess
76
68
  /** JWT Service */
77
69
  jwt?: JWTService<UserSession>
78
- /** The session service used by the application (optional). */
79
- httpSessionService?: HTTPSessionService
80
70
  /** The schema library used to validate data */
81
71
  schemaService?: SchemaService
82
72
  /** The core configuration for the application. */
@@ -94,10 +84,24 @@ export interface CoreSingletonServices<
94
84
  /**
95
85
  * Represents different forms of interaction within Pikku and the outside world.
96
86
  */
97
- export interface PikkuInteractions {
87
+ export interface PikkuInteraction {
98
88
  http?: PikkuHTTP
99
89
  }
100
90
 
91
+ /**
92
+ * A function that can wrap an interaction and be called before or after
93
+ */
94
+ export type PikkuMiddleware<
95
+ SingletonServices extends CoreSingletonServices = CoreSingletonServices,
96
+ UserSession extends CoreUserSession = CoreUserSession,
97
+ > = (
98
+ services: SingletonServices & {
99
+ userSessionService: UserSessionService<UserSession>
100
+ },
101
+ interactions: PikkuInteraction,
102
+ next: () => Promise<void>
103
+ ) => Promise<void>
104
+
101
105
  /**
102
106
  * Represents the core services used by Pikku, including singleton services and the request/response interaction.
103
107
  */
@@ -106,17 +110,14 @@ export type CoreServices<
106
110
  UserSession extends CoreUserSession = CoreUserSession,
107
111
  CoreServices extends Record<string, unknown> = {},
108
112
  > = SingletonServices &
109
- PikkuInteractions & {
113
+ PikkuInteraction & {
110
114
  userSession?: UserSessionService<UserSession>
111
115
  } & CoreServices
112
116
 
113
117
  export type SessionServices<
114
118
  SingletonServices extends CoreSingletonServices = CoreSingletonServices,
115
119
  Services = CoreServices<SingletonServices>,
116
- > = Omit<
117
- Services,
118
- keyof SingletonServices | keyof PikkuInteractions | 'session'
119
- >
120
+ > = Omit<Services, keyof SingletonServices | keyof PikkuInteraction | 'session'>
120
121
 
121
122
  /**
122
123
  * Defines a function type for creating singleton services from the given configuration.
@@ -139,7 +140,7 @@ export type CreateSessionServices<
139
140
  UserSession extends CoreUserSession = CoreUserSession,
140
141
  > = (
141
142
  services: SingletonServices,
142
- interaction: PikkuInteractions,
143
+ interaction: PikkuInteraction,
143
144
  session: UserSession | undefined
144
145
  ) => Promise<SessionServices<Services, SingletonServices>>
145
146
 
@@ -1,4 +1,8 @@
1
- import type { CoreServices, CoreUserSession } from './core.types.js'
1
+ import type {
2
+ CoreServices,
3
+ CoreSingletonServices,
4
+ CoreUserSession,
5
+ } from './core.types.js'
2
6
 
3
7
  /**
4
8
  * Represents a core API function that performs an operation using core services and a user session.
@@ -11,8 +15,8 @@ import type { CoreServices, CoreUserSession } from './core.types.js'
11
15
  export type CoreAPIFunction<
12
16
  In,
13
17
  Out,
14
- Services = CoreServices,
15
- Session = CoreUserSession,
18
+ Services extends CoreSingletonServices = CoreServices,
19
+ Session extends CoreUserSession = CoreUserSession,
16
20
  > = (services: Services, data: In, session: Session) => Promise<Out>
17
21
 
18
22
  /**
@@ -26,8 +30,8 @@ export type CoreAPIFunction<
26
30
  export type CoreAPIFunctionSessionless<
27
31
  In,
28
32
  Out,
29
- Services = CoreServices,
30
- Session = CoreUserSession,
33
+ Services extends CoreSingletonServices = CoreServices,
34
+ Session extends CoreUserSession = CoreUserSession,
31
35
  > = (services: Services, data: In, session?: Session) => Promise<Out>
32
36
 
33
37
  /**
@@ -39,6 +43,6 @@ export type CoreAPIFunctionSessionless<
39
43
  */
40
44
  export type CoreAPIPermission<
41
45
  In = any,
42
- Services = CoreServices,
43
- Session = CoreUserSession,
46
+ Services extends CoreSingletonServices = CoreServices,
47
+ Session extends CoreUserSession = CoreUserSession,
44
48
  > = (services: Services, data: In, session?: Session) => Promise<boolean>