@pikku/core 0.6.16 → 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 +8 -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 +5 -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 +814 -907
  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 +6 -45
  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
@@ -0,0 +1,65 @@
1
+ import {
2
+ CoreConfig,
3
+ CoreSingletonServices,
4
+ CoreUserSession,
5
+ PikkuMiddleware,
6
+ } from '../types/core.types.js'
7
+
8
+ /**
9
+ * API key middleware that retrieves a session from the 'x-api-key' header using a provided callback.
10
+ *
11
+ * @param options.getSessionForAPIKey - A function that returns a session when provided an API key.
12
+ */
13
+ export const authAPIKey = <
14
+ SingletonServices extends CoreSingletonServices<CoreConfig>,
15
+ UserSession extends CoreUserSession,
16
+ >({
17
+ source,
18
+ getSessionForAPIKey,
19
+ jwt,
20
+ }: {
21
+ source: 'header' | 'query' | 'all'
22
+ } & (
23
+ | {
24
+ getSessionForAPIKey?: undefined
25
+ jwt?: true
26
+ }
27
+ | {
28
+ getSessionForAPIKey: (
29
+ services: SingletonServices,
30
+ apiKey: string
31
+ ) => Promise<any>
32
+ jwt?: false
33
+ }
34
+ )) => {
35
+ const middleware: PikkuMiddleware = async (services, { http }, next) => {
36
+ if (!http?.request || services.userSession.get()) {
37
+ return next()
38
+ }
39
+
40
+ let apiKey: string | null = null
41
+ if (source === 'header' || source === 'all') {
42
+ apiKey = http.request.getHeader('x-api-key') as string | null
43
+ }
44
+ if (!apiKey && (source === 'query' || source === 'all')) {
45
+ apiKey = http.request.getQuery().apiKey as string | null
46
+ }
47
+ if (apiKey) {
48
+ let userSession: UserSession | null = null
49
+ if (jwt) {
50
+ if (!services.jwt) {
51
+ throw new Error('JWT service is required for JWT decoding.')
52
+ }
53
+ userSession = await services.jwt.decode(apiKey)
54
+ } else {
55
+ userSession = await getSessionForAPIKey!(services as any, apiKey)
56
+ }
57
+ if (userSession) {
58
+ await services.userSession.set(userSession)
59
+ }
60
+ }
61
+ return next()
62
+ }
63
+
64
+ return middleware
65
+ }
@@ -0,0 +1,65 @@
1
+ import { InvalidSessionError } from '../errors/errors.js'
2
+ import {
3
+ CoreConfig,
4
+ CoreSingletonServices,
5
+ CoreUserSession,
6
+ PikkuMiddleware,
7
+ } from '../types/core.types.js'
8
+
9
+ /**
10
+ * Extracts the Bearer token from the Authorization header
11
+ */
12
+ export const authBearer = <
13
+ SingletonServices extends CoreSingletonServices<CoreConfig>,
14
+ UserSession extends CoreUserSession,
15
+ >({
16
+ token,
17
+ jwt,
18
+ getSession,
19
+ }: {
20
+ token?: {
21
+ value: string
22
+ userSession: UserSession
23
+ }
24
+ jwt?: boolean
25
+ getSession?: (
26
+ services: SingletonServices,
27
+ token: string
28
+ ) => Promise<UserSession> | UserSession
29
+ } = {}): PikkuMiddleware => {
30
+ const middleware: PikkuMiddleware = async (services, { http }, next) => {
31
+ // Skip if session already exists.
32
+ if (!http?.request || services.userSession.get()) {
33
+ return next()
34
+ }
35
+
36
+ const authHeader =
37
+ http.request.getHeader('authorization') ||
38
+ http.request.getHeader('Authorization')
39
+ if (authHeader) {
40
+ const [scheme, bearerToken] = authHeader.split(' ')
41
+ if (scheme !== 'Bearer' || !token || !bearerToken) {
42
+ throw new InvalidSessionError()
43
+ }
44
+ let userSession: UserSession | null = null
45
+ if (jwt) {
46
+ if (!services.jwt) {
47
+ throw new Error('JWT service is required for JWT decoding.')
48
+ }
49
+ userSession = await services.jwt.decode(bearerToken)
50
+ } else if (token) {
51
+ if (bearerToken === token.value) {
52
+ userSession = token.userSession
53
+ }
54
+ } else if (getSession) {
55
+ userSession = await getSession(services as any, token)
56
+ }
57
+
58
+ if (userSession) {
59
+ await services.userSession.set(userSession)
60
+ }
61
+ }
62
+ return next()
63
+ }
64
+ return middleware
65
+ }
@@ -0,0 +1,76 @@
1
+ import {
2
+ CoreConfig,
3
+ CoreSingletonServices,
4
+ CoreUserSession,
5
+ PikkuMiddleware,
6
+ } from '../types/core.types.js'
7
+
8
+ /**
9
+ * Cookie middleware that extracts a session from cookies.
10
+ *
11
+ * @param options.cookieNames - List of cookie names to check.
12
+ * @param options.getSessionForCookieValue - Function to retrieve a session using a cookie value.
13
+ */
14
+ export const authCookie = <
15
+ SingletonServices extends CoreSingletonServices<CoreConfig>,
16
+ UserSession extends CoreUserSession,
17
+ >({
18
+ cookieNames,
19
+ getSessionForCookieValue,
20
+ jwt,
21
+ }: {
22
+ cookieNames: string[]
23
+ } & (
24
+ | {
25
+ getSessionForCookieValue: (
26
+ services: SingletonServices,
27
+ cookieValue: string,
28
+ cookieName: string
29
+ ) => Promise<UserSession>
30
+ jwt?: false
31
+ }
32
+ | {
33
+ getSessionForCookieValue?: undefined
34
+ jwt: true
35
+ }
36
+ )): PikkuMiddleware => {
37
+ const middleware: PikkuMiddleware = async (services, { http }, next) => {
38
+ if (!http?.request || services.userSession.get()) {
39
+ return next()
40
+ }
41
+
42
+ const cookies = http.request.getCookies()
43
+ if (cookies) {
44
+ let cookieName: string | undefined
45
+ for (const name of cookieNames) {
46
+ if (cookies[name]) {
47
+ cookieName = name
48
+ break
49
+ }
50
+ }
51
+ if (cookieName) {
52
+ let userSession: UserSession | null = null
53
+ const cookieValue = cookies[cookieName]
54
+ if (cookieValue) {
55
+ if (jwt) {
56
+ if (!services.jwt) {
57
+ throw new Error('JWT service is required for JWT decoding.')
58
+ }
59
+ userSession = await services.jwt.decode(cookieValue)
60
+ } else if (getSessionForCookieValue) {
61
+ userSession = await getSessionForCookieValue(
62
+ services as any,
63
+ cookieValue,
64
+ cookieName
65
+ )
66
+ }
67
+ if (userSession) {
68
+ await services.userSession.set(userSession)
69
+ }
70
+ }
71
+ }
72
+ }
73
+ return next()
74
+ }
75
+ return middleware
76
+ }
@@ -1,4 +1,3 @@
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'
1
+ export * from './auth-apikey.js'
2
+ export * from './auth-cookie.js'
3
+ export * from './auth-bearer.js'
@@ -0,0 +1,13 @@
1
+ import { RequestTimeoutError } from '../errors/errors.js'
2
+ import { PikkuMiddleware } from '../types/core.types.js'
3
+
4
+ export const timeout = (timeout: number) => {
5
+ const middleware: PikkuMiddleware = async (_services, _interaction, next) => {
6
+ setTimeout(() => {
7
+ throw new RequestTimeoutError()
8
+ }, timeout)
9
+
10
+ await next()
11
+ }
12
+ return middleware
13
+ }
@@ -24,7 +24,7 @@ import {
24
24
  */
25
25
  export const runMiddleware = async (
26
26
  services: CoreSingletonServices & {
27
- userSessionService: UserSessionService<any>
27
+ userSession: UserSessionService<any>
28
28
  },
29
29
  interaction: PikkuInteraction,
30
30
  middlewares: PikkuMiddleware[],
@@ -0,0 +1,70 @@
1
+ import { ChannelsMeta, CoreAPIChannels } from './channel/channel.types.js'
2
+ import {
3
+ CoreHTTPFunctionRoutes,
4
+ HTTPRoutesMeta,
5
+ } from './http/http-routes.types.js'
6
+ import { PikkuMiddleware } from './types/core.types.js'
7
+ import {
8
+ CoreScheduledTask,
9
+ ScheduledTasksMeta,
10
+ } from './scheduler/scheduler.types.js'
11
+ import { ErrorDetails, PikkuError } from './errors/error-handler.js'
12
+
13
+ interface PikkuState {
14
+ http: {
15
+ middleware: Array<{ route: string; middleware: PikkuMiddleware[] }>
16
+ routes: CoreHTTPFunctionRoutes
17
+ meta: HTTPRoutesMeta
18
+ }
19
+ channel: {
20
+ channels: CoreAPIChannels
21
+ meta: ChannelsMeta
22
+ }
23
+ scheduler: {
24
+ tasks: Map<string, CoreScheduledTask>
25
+ meta: ScheduledTasksMeta
26
+ }
27
+ errors: {
28
+ errors: Map<PikkuError, ErrorDetails>
29
+ }
30
+ }
31
+
32
+ export const resetPikkuState = () => {
33
+ globalThis.pikkuState = {
34
+ http: {
35
+ middleware: [],
36
+ routes: [],
37
+ meta: [],
38
+ },
39
+ channel: {
40
+ channels: [],
41
+ meta: [],
42
+ },
43
+ scheduler: {
44
+ tasks: new Map(),
45
+ meta: [],
46
+ },
47
+ errors: {
48
+ // We keep errors since they are registered globally
49
+ errors: globalThis.pikkuState?.errors?.errors || new Map(),
50
+ },
51
+ }
52
+ }
53
+
54
+ if (!globalThis.pikkuState) {
55
+ resetPikkuState()
56
+ }
57
+
58
+ export const pikkuState = <
59
+ Type extends keyof PikkuState,
60
+ Content extends keyof PikkuState[Type],
61
+ >(
62
+ type: Type,
63
+ content: Content,
64
+ value?: PikkuState[Type][Content]
65
+ ): PikkuState[Type][Content] => {
66
+ if (value) {
67
+ globalThis.pikkuState[type][content] = value
68
+ }
69
+ return globalThis.pikkuState[type][content]
70
+ }
@@ -1,11 +1,5 @@
1
1
  export * from './scheduler.types.js'
2
2
 
3
- export {
4
- runScheduledTask,
5
- getScheduledTasks,
6
- addScheduledTask,
7
- clearScheduledTasks,
8
- setScheduledTasksMeta,
9
- } from './scheduler-runner.js'
3
+ export { runScheduledTask, addScheduledTask } from './scheduler-runner.js'
10
4
 
11
5
  export * from './log-schedulers.js'
@@ -1,12 +1,12 @@
1
+ import { pikkuState } from '../pikku-state.js'
1
2
  import { Logger } from '../services/index.js'
2
- import { getScheduledTasks } from './scheduler-runner.js'
3
3
 
4
4
  /**
5
5
  * Logs all the loaded scheduled tasks.
6
6
  * @param logger - A logger for logging information.
7
7
  */
8
8
  export const logSchedulers = (logger: Logger) => {
9
- const { scheduledTasks } = getScheduledTasks()
9
+ const scheduledTasks = pikkuState('scheduler', 'tasks')
10
10
  if (scheduledTasks.size === 0) {
11
11
  logger.info('No scheduled tasks added')
12
12
  return
@@ -4,14 +4,12 @@ import type {
4
4
  CoreUserSession,
5
5
  CreateSessionServices,
6
6
  } from '../types/core.types.js'
7
- import type {
8
- CoreScheduledTask,
9
- ScheduledTasksMeta,
10
- } from './scheduler.types.js'
7
+ import type { CoreScheduledTask } from './scheduler.types.js'
11
8
  import type { CoreAPIFunctionSessionless } from '../types/functions.types.js'
12
9
  import crypto from 'crypto'
13
10
  import { getErrorResponse } from '../errors/error-handler.js'
14
11
  import { closeSessionServices } from '../utils.js'
12
+ import { pikkuState } from '../pikku-state.js'
15
13
 
16
14
  export type RunScheduledTasksParams = {
17
15
  name: string
@@ -24,53 +22,16 @@ export type RunScheduledTasksParams = {
24
22
  >
25
23
  }
26
24
 
27
- if (!global.pikku?.scheduledTasks) {
28
- global.pikku = global.pikku || {}
29
- global.pikku.scheduledTasks = new Map<string, CoreScheduledTask>()
30
- global.pikku.scheduledTasksMeta = []
31
- }
32
-
33
- const scheduledTasks = (): Map<string, CoreScheduledTask> => {
34
- return global.pikku.scheduledTasks
35
- }
36
-
37
- const scheduledTasksMeta = (data?: ScheduledTasksMeta) => {
38
- if (data) {
39
- global.pikku.scheduledTasksMeta = data
40
- }
41
- return global.pikku.scheduledTasksMeta
42
- }
43
-
44
25
  export const addScheduledTask = <
45
26
  APIFunction extends CoreAPIFunctionSessionless<void, void>,
46
27
  >(
47
28
  scheduledTask: CoreScheduledTask<APIFunction>
48
29
  ) => {
49
- if (scheduledTasks().has(scheduledTask.name)) {
30
+ const tasks = pikkuState('scheduler', 'tasks')
31
+ if (tasks.has(scheduledTask.name)) {
50
32
  throw new Error(`Scheduled task already exists: ${scheduledTask.name}`)
51
33
  }
52
- scheduledTasks().set(scheduledTask.name, scheduledTask)
53
- }
54
-
55
- export const clearScheduledTasks = () => {
56
- scheduledTasks().clear()
57
- }
58
-
59
- export const setScheduledTasksMeta = (
60
- _scheduledTasksMeta: ScheduledTasksMeta
61
- ) => {
62
- scheduledTasksMeta(_scheduledTasksMeta)
63
- }
64
-
65
- /**
66
- * Returns all the cron jobs
67
- * @internal
68
- */
69
- export const getScheduledTasks = () => {
70
- return {
71
- scheduledTasks: scheduledTasks(),
72
- scheduledTasksMeta: scheduledTasksMeta(),
73
- }
34
+ tasks.set(scheduledTask.name, scheduledTask)
74
35
  }
75
36
 
76
37
  class ScheduledTaskNotFoundError extends Error {
@@ -94,7 +55,7 @@ export async function runScheduledTask<
94
55
  const trackerId: string = crypto.randomUUID().toString()
95
56
 
96
57
  try {
97
- const task = scheduledTasks().get(name)
58
+ const task = pikkuState('scheduler', 'tasks').get(name)
98
59
 
99
60
  if (!task) {
100
61
  throw new ScheduledTaskNotFoundError(`Scheduled task not found: ${name}`)
package/src/schema.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Logger } from './services/logger.js'
2
- import { getRoutes } from './http/http-route-runner.js'
3
2
  import { SchemaService } from './services/schema-service.js'
4
3
  import { UnprocessableContentError } from './errors/errors.js'
4
+ import { pikkuState } from './pikku-state.js'
5
5
 
6
6
  /**
7
7
  * Retrieves the global schemas map.
@@ -58,7 +58,7 @@ const validateAllSchemasLoaded = (
58
58
  logger: Logger,
59
59
  schemaService: SchemaService
60
60
  ) => {
61
- const { routesMeta } = getRoutes()
61
+ const routesMeta = pikkuState('http', 'meta')
62
62
  const validators = schemaService.getSchemaNames()
63
63
 
64
64
  const missingSchemas: string[] = []
@@ -1,10 +1,7 @@
1
- import { CoreUserSession } from '../types/core.types.js'
2
-
3
1
  /**
4
2
  * Interface for handling JSON Web Tokens (JWT).
5
- * @template UserSession - The type of the user session.
6
3
  */
7
- export interface JWTService<UserSession = CoreUserSession> {
4
+ export interface JWTService {
8
5
  /**
9
6
  * Encodes a payload into a JWT.
10
7
  * @param expiresIn - The expiration time of the token.
@@ -25,12 +22,4 @@ export interface JWTService<UserSession = CoreUserSession> {
25
22
  invalidHashError?: Error,
26
23
  debug?: boolean
27
24
  ) => Promise<T>
28
-
29
- /**
30
- * Decodes a user session from a JWT.
31
- * @param jwtToken - The JWT representing the user session.
32
- * @param debug - An optional flag for debugging.
33
- * @returns A promise that resolves to the decoded user session.
34
- */
35
- decodeSession: (jwtToken: string, debug?: any) => Promise<UserSession>
36
25
  }
@@ -34,16 +34,18 @@ export class RemoteUserSessionService<UserSession extends CoreUserSession>
34
34
  public session: UserSession | undefined = undefined
35
35
  ) {}
36
36
 
37
- public async set(session: UserSession) {
38
- await this.channelStore.setUserSession(this.channelId, session)
37
+ public set(session: UserSession) {
38
+ return this.channelStore.setUserSession(this.channelId, session)
39
39
  }
40
40
 
41
- public async clear() {
42
- await this.channelStore.setUserSession(this.channelId, undefined)
41
+ public clear() {
42
+ return this.channelStore.setUserSession(this.channelId, null)
43
43
  }
44
44
 
45
45
  public async get(): Promise<UserSession | undefined> {
46
- const channel = await this.channelStore.getChannel(this.channelId)
47
- return channel.userSession
46
+ const { session } = await this.channelStore.getChannelAndSession(
47
+ this.channelId
48
+ )
49
+ return session
48
50
  }
49
51
  }
@@ -61,12 +61,9 @@ export interface CoreUserSession {}
61
61
  /**
62
62
  * Interface for core singleton services provided by Pikku.
63
63
  */
64
- export interface CoreSingletonServices<
65
- Config extends CoreConfig = CoreConfig,
66
- UserSession extends CoreUserSession = CoreUserSession,
67
- > {
64
+ export interface CoreSingletonServices<Config extends CoreConfig = CoreConfig> {
68
65
  /** JWT Service */
69
- jwt?: JWTService<UserSession>
66
+ jwt?: JWTService
70
67
  /** The schema library used to validate data */
71
68
  schemaService?: SchemaService
72
69
  /** The core configuration for the application. */
@@ -96,7 +93,7 @@ export type PikkuMiddleware<
96
93
  UserSession extends CoreUserSession = CoreUserSession,
97
94
  > = (
98
95
  services: SingletonServices & {
99
- userSessionService: UserSessionService<UserSession>
96
+ userSession: UserSessionService<UserSession>
100
97
  },
101
98
  interactions: PikkuInteraction,
102
99
  next: () => Promise<void>