@pikku/core 0.6.27 → 0.7.1

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 (75) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/dist/channel/channel-handler.js +16 -38
  3. package/dist/channel/channel-runner.d.ts +7 -8
  4. package/dist/channel/channel-runner.js +58 -7
  5. package/dist/channel/channel.types.d.ts +14 -27
  6. package/dist/channel/local/local-channel-runner.js +9 -4
  7. package/dist/channel/log-channels.js +4 -4
  8. package/dist/channel/serverless/serverless-channel-runner.js +17 -6
  9. package/dist/function/function-runner.d.ts +12 -0
  10. package/dist/function/function-runner.js +38 -0
  11. package/dist/{types → function}/functions.types.d.ts +12 -7
  12. package/dist/function/index.d.ts +2 -0
  13. package/dist/function/index.js +2 -0
  14. package/dist/handle-error.d.ts +1 -1
  15. package/dist/http/{http-route-runner.d.ts → http-runner.d.ts} +4 -4
  16. package/dist/http/{http-route-runner.js → http-runner.js} +81 -43
  17. package/dist/http/{http-routes.types.d.ts → http.types.d.ts} +41 -12
  18. package/dist/http/index.d.ts +2 -2
  19. package/dist/http/index.js +1 -1
  20. package/dist/http/log-http-routes.js +7 -5
  21. package/dist/http/pikku-fetch-http-request.d.ts +1 -1
  22. package/dist/http/pikku-fetch-http-response.d.ts +4 -2
  23. package/dist/http/pikku-fetch-http-response.js +64 -12
  24. package/dist/index.d.ts +3 -2
  25. package/dist/index.js +3 -2
  26. package/dist/middleware/auth-apikey.js +5 -4
  27. package/dist/middleware/auth-bearer.js +5 -4
  28. package/dist/middleware/auth-cookie.js +10 -9
  29. package/dist/permissions.d.ts +2 -2
  30. package/dist/pikku-function.d.ts +1 -0
  31. package/dist/pikku-function.js +1 -0
  32. package/dist/pikku-state.d.ts +10 -5
  33. package/dist/pikku-state.js +7 -3
  34. package/dist/scheduler/scheduler-runner.d.ts +1 -1
  35. package/dist/scheduler/scheduler-runner.js +24 -6
  36. package/dist/scheduler/scheduler.types.d.ts +3 -2
  37. package/dist/services/content-service.d.ts +33 -37
  38. package/dist/types/core.types.d.ts +15 -1
  39. package/dist/utils.d.ts +2 -0
  40. package/dist/utils.js +15 -0
  41. package/lcov.info +1240 -965
  42. package/package.json +2 -1
  43. package/src/channel/channel-handler.ts +16 -78
  44. package/src/channel/channel-runner.ts +72 -17
  45. package/src/channel/channel.types.ts +25 -63
  46. package/src/channel/local/local-channel-runner.test.ts +10 -11
  47. package/src/channel/local/local-channel-runner.ts +20 -7
  48. package/src/channel/log-channels.ts +4 -4
  49. package/src/channel/serverless/serverless-channel-runner.ts +26 -15
  50. package/src/function/function-runner.ts +91 -0
  51. package/src/{types → function}/functions.types.ts +25 -11
  52. package/src/function/index.ts +2 -0
  53. package/src/handle-error.ts +1 -1
  54. package/src/http/{http-route-runner.test.ts → http-runner.test.ts} +29 -6
  55. package/src/http/{http-route-runner.ts → http-runner.ts} +106 -73
  56. package/src/http/{http-routes.types.ts → http.types.ts} +55 -16
  57. package/src/http/index.ts +2 -2
  58. package/src/http/log-http-routes.ts +7 -5
  59. package/src/http/pikku-fetch-http-request.ts +1 -5
  60. package/src/http/pikku-fetch-http-response.ts +67 -13
  61. package/src/index.ts +3 -2
  62. package/src/middleware/auth-apikey.ts +5 -4
  63. package/src/middleware/auth-bearer.ts +5 -4
  64. package/src/middleware/auth-cookie.ts +14 -9
  65. package/src/permissions.ts +2 -4
  66. package/src/pikku-function.ts +1 -0
  67. package/src/pikku-state.ts +25 -9
  68. package/src/scheduler/scheduler-runner.ts +31 -13
  69. package/src/scheduler/scheduler.types.ts +12 -8
  70. package/src/services/content-service.ts +36 -40
  71. package/src/types/core.types.ts +20 -1
  72. package/src/utils.ts +19 -0
  73. package/tsconfig.tsbuildinfo +1 -1
  74. /package/dist/{types → function}/functions.types.js +0 -0
  75. /package/dist/http/{http-routes.types.js → http.types.js} +0 -0
@@ -1,6 +1,6 @@
1
- import { SerializeOptions } from 'cookie'
2
- import { PikkuError } from '../errors/error-handler.js'
3
- import {
1
+ import type { SerializeOptions } from 'cookie'
2
+ import type { PikkuError } from '../errors/error-handler.js'
3
+ import type {
4
4
  APIDocs,
5
5
  CoreServices,
6
6
  CoreSingletonServices,
@@ -8,11 +8,12 @@ import {
8
8
  CreateSessionServices,
9
9
  PikkuMiddleware,
10
10
  } from '../types/core.types.js'
11
- import {
11
+ import type {
12
12
  CoreAPIFunction,
13
13
  CoreAPIFunctionSessionless,
14
14
  CoreAPIPermission,
15
- } from '../types/functions.types.js'
15
+ CorePermissionGroup,
16
+ } from '../function/functions.types.js'
16
17
 
17
18
  type ExtractRouteParams<S extends string> =
18
19
  S extends `${string}:${infer Param}/${infer Rest}`
@@ -32,6 +33,7 @@ export type RunRouteOptions = Partial<{
32
33
  logWarningsForStatusCodes: number[]
33
34
  coerceDataFromSchema: boolean
34
35
  bubbleErrors: boolean
36
+ generateRequestId: () => string
35
37
  }>
36
38
 
37
39
  export type RunRouteParams = {
@@ -117,10 +119,11 @@ export type CoreHTTPFunctionRoute<
117
119
  route: R
118
120
  method: HTTPMethod
119
121
  func: APIFunction
120
- permissions?: Record<string, APIPermission[] | APIPermission>
122
+ permissions?: CorePermissionGroup<APIPermission>
121
123
  auth?: true
122
124
  tags?: string[]
123
125
  middleware?: APIMiddleware[]
126
+ sse?: undefined
124
127
  })
125
128
  | (CoreHTTPFunction & {
126
129
  route: R
@@ -130,16 +133,38 @@ export type CoreHTTPFunctionRoute<
130
133
  auth?: false
131
134
  tags?: string[]
132
135
  middleware?: APIMiddleware[]
136
+ sse?: undefined
137
+ })
138
+ | (CoreHTTPFunction & {
139
+ route: R
140
+ method: 'get'
141
+ func: APIFunction
142
+ permissions?: CorePermissionGroup<APIPermission>
143
+ auth?: true
144
+ sse?: boolean
145
+ tags?: string[]
146
+ middleware?: APIMiddleware[]
147
+ })
148
+ | (CoreHTTPFunction & {
149
+ route: R
150
+ method: 'get'
151
+ func: APIFunctionSessionless
152
+ permissions?: undefined
153
+ auth?: false
154
+ sse?: boolean
155
+ tags?: string[]
156
+ middleware?: APIMiddleware[]
133
157
  })
134
158
  | (CoreHTTPFunction & {
135
159
  route: R
136
160
  method: 'post'
137
161
  func: APIFunction
138
- permissions?: Record<string, APIPermission[] | APIPermission>
162
+ permissions?: CorePermissionGroup<APIPermission>
139
163
  auth?: true
140
164
  query?: Array<keyof In>
141
165
  tags?: string[]
142
166
  middleware?: APIMiddleware[]
167
+ sse?: undefined
143
168
  })
144
169
  | (CoreHTTPFunction & {
145
170
  route: R
@@ -150,15 +175,9 @@ export type CoreHTTPFunctionRoute<
150
175
  query?: Array<keyof In>
151
176
  tags?: string[]
152
177
  middleware?: APIMiddleware[]
178
+ sse?: undefined
153
179
  })
154
180
 
155
- /**
156
- * Represents an array of core API routes.
157
- */
158
- export type CoreHTTPFunctionRoutes = Array<
159
- CoreHTTPFunctionRoute<any, any, string>
160
- >
161
-
162
181
  /**
163
182
  * Represents the input types for route metadata, including parameters, query, and body types.
164
183
  */
@@ -171,7 +190,8 @@ export type HTTPFunctionMetaInputTypes = {
171
190
  /**
172
191
  * Represents metadata for a set of routes, including route details, methods, input/output types, and documentation.
173
192
  */
174
- export type HTTPRoutesMeta = Array<{
193
+ export type HTTPRouteMeta = {
194
+ pikkuFuncName: string
175
195
  route: string
176
196
  method: HTTPMethod
177
197
  params?: string[]
@@ -181,6 +201,14 @@ export type HTTPRoutesMeta = Array<{
181
201
  inputTypes?: HTTPFunctionMetaInputTypes
182
202
  docs?: APIDocs
183
203
  tags?: string[]
204
+ sse?: true
205
+ }
206
+ export type HTTPRoutesMeta = Array<HTTPRouteMeta>
207
+
208
+ export type HTTPFunctionsMeta = Array<{
209
+ name: string
210
+ inputs: string[] | null
211
+ outputs: string[] | null
184
212
  }>
185
213
 
186
214
  export type HTTPRouteMiddleware = {
@@ -205,7 +233,18 @@ export interface PikkuHTTPResponse {
205
233
  status(code: number): this
206
234
  cookie(name: string, value: string | null, options: SerializeOptions): this
207
235
  header(name: string, value: string | string[]): this
208
- arrayBuffer(data: XMLHttpRequestBodyInit): this
236
+ arrayBuffer(
237
+ data:
238
+ | ArrayBuffer
239
+ | ArrayBufferView
240
+ | Blob
241
+ | string
242
+ | FormData
243
+ | URLSearchParams
244
+ | ReadableStream
245
+ ): this
209
246
  json(data: unknown): this
210
247
  redirect(location: string, status?: number): this
248
+ close?: () => void
249
+ setMode?: (mode: 'stream') => void
211
250
  }
package/src/http/index.ts CHANGED
@@ -3,6 +3,6 @@ export * from './pikku-fetch-http-response.js'
3
3
  export * from './incomingmessage-to-request-convertor.js'
4
4
  export * from './log-http-routes.js'
5
5
 
6
- export { fetch, fetchData, addRoute } from './http-route-runner.js'
6
+ export { fetch, fetchData, addHTTPRoute } from './http-runner.js'
7
7
 
8
- export type * from './http-routes.types.js'
8
+ export type * from './http.types.js'
@@ -6,15 +6,17 @@ import { Logger } from '../services/index.js'
6
6
  * @param logger - A logger for logging information.
7
7
  */
8
8
  export const logRoutes = (logger: Logger) => {
9
- const routes = pikkuState('http', 'routes')
10
- if (routes.length === 0) {
9
+ const routesByType = pikkuState('http', 'routes')
10
+ if (routesByType.size === 0) {
11
11
  logger.info('No routes added')
12
12
  return
13
13
  }
14
14
 
15
15
  let routesMessage = 'Routes loaded:'
16
- for (const { method, route } of routes) {
17
- routesMessage += `\n\t- ${method.toUpperCase()} -> ${route}`
18
- }
16
+ routesByType.forEach((routes) => {
17
+ routes.forEach((route) => {
18
+ routesMessage += `\n\t- ${route.method.toUpperCase()} -> ${route.route}`
19
+ })
20
+ })
19
21
  logger.info(routesMessage)
20
22
  }
@@ -1,10 +1,6 @@
1
1
  import { parse as parseQuery } from 'picoquery'
2
2
  import { parse as parseCookie } from 'cookie'
3
- import {
4
- HTTPMethod,
5
- PikkuHTTPRequest,
6
- PikkuQuery,
7
- } from './http-routes.types.js'
3
+ import { HTTPMethod, PikkuHTTPRequest, PikkuQuery } from './http.types.js'
8
4
  import { UnprocessableContentError } from '../errors/errors.js'
9
5
 
10
6
  /**
@@ -1,4 +1,4 @@
1
- import { PikkuHTTPResponse } from './http-routes.types.js'
1
+ import { PikkuHTTPResponse } from './http.types.js'
2
2
  import {
3
3
  SerializeOptions as CookieSerializeOptions,
4
4
  serialize as serializeCookie,
@@ -10,6 +10,16 @@ export class PikkuFetchHTTPResponse implements PikkuHTTPResponse {
10
10
  #cookies = new Map<string, { value: string; flags: CookieSerializeOptions }>()
11
11
 
12
12
  #body: BodyInit | null = null
13
+ #responseMode: 'stream' | null = null
14
+ #send: ((data: string) => void) | null = null
15
+ #close: (() => void) | null = null
16
+
17
+ public setMode(mode: 'stream') {
18
+ this.#responseMode = 'stream'
19
+ if (mode === 'stream') {
20
+ this.#body = this.createStream()
21
+ }
22
+ }
13
23
 
14
24
  public status(code: number): this {
15
25
  this.#statusCode = code
@@ -36,33 +46,49 @@ export class PikkuFetchHTTPResponse implements PikkuHTTPResponse {
36
46
  }
37
47
 
38
48
  public arrayBuffer(data: XMLHttpRequestBodyInit): this {
39
- this.#body = data
40
- this.header('Content-Type', 'application/octet-stream')
49
+ if (this.#responseMode === 'stream') {
50
+ this.#send!(data as string)
51
+ } else {
52
+ this.#body = data
53
+ this.header('Content-Type', 'application/octet-stream')
54
+ }
41
55
  return this
42
56
  }
43
57
 
44
58
  public json(data: unknown): this {
45
- this.#body = JSON.stringify(data)
46
- this.header('Content-Type', 'application/json')
59
+ if (this.#responseMode === 'stream') {
60
+ this.#send!(JSON.stringify(data))
61
+ } else {
62
+ this.#body = JSON.stringify(data)
63
+ this.header('Content-Type', 'application/json')
64
+ }
47
65
  return this
48
66
  }
49
67
 
50
68
  public text(content: string): this {
51
- this.#body = content
52
- this.header('Content-Type', 'text/plain')
69
+ if (this.#responseMode === 'stream') {
70
+ this.#send!(content)
71
+ } else {
72
+ this.#body = content
73
+ this.header('Content-Type', 'text/plain')
74
+ }
53
75
  return this
54
76
  }
55
77
 
56
78
  public html(content: string): this {
57
- this.#body = content
58
- this.header('Content-Type', 'text/html')
79
+ if (this.#responseMode === 'stream') {
80
+ this.#send!(content)
81
+ } else {
82
+ this.#body = content
83
+ this.header('Content-Type', 'text/html')
84
+ }
59
85
  return this
60
86
  }
61
87
 
62
- public body(body: BodyInit): this {
63
- this.#body = body
64
- return this
65
- }
88
+ // public body(body: BodyInit): this {
89
+ // this.#body = body
90
+ // return this
91
+ // }
66
92
 
67
93
  public redirect(location: string, status: number = 302): this {
68
94
  this.#statusCode = status
@@ -70,6 +96,13 @@ export class PikkuFetchHTTPResponse implements PikkuHTTPResponse {
70
96
  return this
71
97
  }
72
98
 
99
+ public close(): this {
100
+ if (this.#close) {
101
+ this.#close()
102
+ }
103
+ return this
104
+ }
105
+
73
106
  public toResponse(args?: Record<string, any>): Response {
74
107
  const cookieHeader = Array.from(this.#cookies.entries()).map(
75
108
  ([name, { value, flags }]) => serializeCookie(name, value, flags)
@@ -81,4 +114,25 @@ export class PikkuFetchHTTPResponse implements PikkuHTTPResponse {
81
114
  headers: this.#headers,
82
115
  })
83
116
  }
117
+
118
+ private createStream(): ReadableStream<Uint8Array> {
119
+ const encoder = new TextEncoder()
120
+ return new ReadableStream({
121
+ start: (controller) => {
122
+ const send = (data: string) => {
123
+ controller.enqueue(encoder.encode(`data: ${data}\n\n`))
124
+ }
125
+
126
+ const close = () => {
127
+ controller.close()
128
+ }
129
+
130
+ this.#send = send
131
+ this.#close = close
132
+
133
+ // Force initial flush
134
+ controller.enqueue(encoder.encode(':\n\n'))
135
+ },
136
+ })
137
+ }
84
138
  }
package/src/index.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * @module @pikku/core
3
3
  */
4
4
  export * from './types/core.types.js'
5
- export * from './types/functions.types.js'
5
+ export * from './function/index.js'
6
6
  export * from './pikku-request.js'
7
7
  export * from './pikku-response.js'
8
8
  export * from './services/index.js'
@@ -12,8 +12,9 @@ export * from './scheduler/index.js'
12
12
  export * from './errors/index.js'
13
13
  export * from './middleware/index.js'
14
14
  export * from './time-utils.js'
15
+ export * from './utils.js'
15
16
  export { pikkuState } from './pikku-state.js'
16
17
  export { runMiddleware } from './middleware-runner.js'
17
- export { addRoute, addMiddleware } from './http/http-route-runner.js'
18
+ export { addHTTPRoute, addMiddleware } from './http/http-runner.js'
18
19
  export { addChannel } from './channel/channel-runner.js'
19
20
  export { addScheduledTask } from './scheduler/scheduler-runner.js'
@@ -33,7 +33,8 @@ export const authAPIKey = <
33
33
  }
34
34
  )) => {
35
35
  const middleware: PikkuMiddleware = async (services, { http }, next) => {
36
- if (!http?.request || services.userSession.get()) {
36
+ const { userSession: userSessionService, jwt: jwtService } = services
37
+ if (!http?.request || userSessionService.get()) {
37
38
  return next()
38
39
  }
39
40
 
@@ -47,15 +48,15 @@ export const authAPIKey = <
47
48
  if (apiKey) {
48
49
  let userSession: UserSession | null = null
49
50
  if (jwt) {
50
- if (!services.jwt) {
51
+ if (!jwtService) {
51
52
  throw new Error('JWT service is required for JWT decoding.')
52
53
  }
53
- userSession = await services.jwt.decode(apiKey)
54
+ userSession = await jwtService.decode(apiKey)
54
55
  } else {
55
56
  userSession = await getSessionForAPIKey!(services as any, apiKey)
56
57
  }
57
58
  if (userSession) {
58
- services.userSession.setInitial(userSession)
59
+ userSessionService.setInitial(userSession)
59
60
  }
60
61
  }
61
62
  return next()
@@ -28,8 +28,9 @@ export const authBearer = <
28
28
  ) => Promise<UserSession> | UserSession
29
29
  } = {}): PikkuMiddleware => {
30
30
  const middleware: PikkuMiddleware = async (services, { http }, next) => {
31
+ const { userSession: userSessionService, jwt: jwtService } = services
31
32
  // Skip if session already exists.
32
- if (!http?.request || services.userSession.get()) {
33
+ if (!http?.request || userSessionService.get()) {
33
34
  return next()
34
35
  }
35
36
 
@@ -43,10 +44,10 @@ export const authBearer = <
43
44
  }
44
45
  let userSession: UserSession | null = null
45
46
  if (jwt) {
46
- if (!services.jwt) {
47
+ if (!jwtService) {
47
48
  throw new Error('JWT service is required for JWT decoding.')
48
49
  }
49
- userSession = await services.jwt.decode(bearerToken)
50
+ userSession = await jwtService.decode(bearerToken)
50
51
  } else if (token) {
51
52
  if (bearerToken === token.value) {
52
53
  userSession = token.userSession
@@ -56,7 +57,7 @@ export const authBearer = <
56
57
  }
57
58
 
58
59
  if (userSession) {
59
- services.userSession.setInitial(userSession)
60
+ userSessionService.setInitial(userSession)
60
61
  }
61
62
  }
62
63
  return next()
@@ -44,7 +44,12 @@ export const authCookie = <
44
44
  }
45
45
  )): PikkuMiddleware => {
46
46
  const middleware: PikkuMiddleware = async (services, { http }, next) => {
47
- if (!http?.request || services.userSession.get()) {
47
+ const {
48
+ userSession: userSessionService,
49
+ jwt: jwtService,
50
+ logger,
51
+ } = services
52
+ if (!http?.request || userSessionService.get()) {
48
53
  return next()
49
54
  }
50
55
 
@@ -52,10 +57,10 @@ export const authCookie = <
52
57
  const cookieValue = http.request.cookie(name)
53
58
  if (cookieValue) {
54
59
  if (jwt) {
55
- if (!services.jwt) {
60
+ if (!jwtService) {
56
61
  throw new Error('JWT service is required for JWT decoding.')
57
62
  }
58
- userSession = await services.jwt.decode(cookieValue)
63
+ userSession = await jwtService.decode(cookieValue)
59
64
  } else if (getSessionForCookieValue) {
60
65
  userSession = await getSessionForCookieValue(
61
66
  services as any,
@@ -66,7 +71,7 @@ export const authCookie = <
66
71
  }
67
72
 
68
73
  if (userSession) {
69
- services.userSession.setInitial(userSession)
74
+ userSessionService.setInitial(userSession)
70
75
  }
71
76
  await next()
72
77
 
@@ -75,22 +80,22 @@ export const authCookie = <
75
80
  return
76
81
  }
77
82
 
78
- if (services.userSession.sessionChanged) {
79
- const session = services.userSession.get()
83
+ if (userSessionService.sessionChanged) {
84
+ const session = userSessionService.get()
80
85
  if (jwt) {
81
- if (!services.jwt) {
86
+ if (!jwtService) {
82
87
  throw new Error('JWT service is required for JWT encoding.')
83
88
  }
84
89
  http.response.cookie(
85
90
  name,
86
- await services.jwt.encode(expiresIn, session),
91
+ await jwtService.encode(expiresIn, session),
87
92
  {
88
93
  ...options,
89
94
  expires: getRelativeTimeOffsetFromNow(expiresIn),
90
95
  }
91
96
  )
92
97
  } else {
93
- services.logger.warn('No JWT service available, unable to set cookie')
98
+ logger.warn('No JWT service available, unable to set cookie')
94
99
  }
95
100
  }
96
101
  }
@@ -1,5 +1,5 @@
1
1
  import { CoreServices, CoreUserSession } from './types/core.types.js'
2
- import { CoreAPIPermission } from './types/functions.types.js'
2
+ import { CorePermissionGroup } from './function/functions.types.js'
3
3
 
4
4
  /**
5
5
  * This function validates permissions by iterating over permission groups and executing the corresponding permission functions. If all functions in at least one group return true, the permission is considered valid.
@@ -9,9 +9,7 @@ import { CoreAPIPermission } from './types/functions.types.js'
9
9
  * @returns A promise that resolves to void.
10
10
  */
11
11
  export const verifyPermissions = async (
12
- permissions:
13
- | Record<string, CoreAPIPermission<any>[] | CoreAPIPermission<any>>
14
- | undefined,
12
+ permissions: CorePermissionGroup,
15
13
  services: CoreServices,
16
14
  data: any,
17
15
  session?: CoreUserSession
@@ -0,0 +1 @@
1
+ export const callFunction = () => {}
@@ -1,23 +1,35 @@
1
- import { ChannelsMeta, CoreAPIChannels } from './channel/channel.types.js'
1
+ import { ChannelsMeta, CoreAPIChannel } from './channel/channel.types.js'
2
2
  import {
3
- CoreHTTPFunctionRoutes,
3
+ CoreHTTPFunctionRoute,
4
+ HTTPMethod,
4
5
  HTTPRoutesMeta,
5
- } from './http/http-routes.types.js'
6
- import { PikkuMiddleware } from './types/core.types.js'
6
+ } from './http/http.types.js'
7
+ import { FunctionsMeta, PikkuMiddleware } from './types/core.types.js'
7
8
  import {
8
9
  CoreScheduledTask,
9
10
  ScheduledTasksMeta,
10
11
  } from './scheduler/scheduler.types.js'
11
12
  import { ErrorDetails, PikkuError } from './errors/error-handler.js'
13
+ import {
14
+ CoreAPIFunction,
15
+ CoreAPIFunctionSessionless,
16
+ } from './function/functions.types.js'
12
17
 
13
18
  interface PikkuState {
19
+ functions: {
20
+ meta: FunctionsMeta
21
+ nameToFunction: Map<
22
+ string,
23
+ CoreAPIFunction<any, any> | CoreAPIFunctionSessionless<any, any>
24
+ >
25
+ }
14
26
  http: {
15
27
  middleware: Array<{ route: string; middleware: PikkuMiddleware[] }>
16
- routes: CoreHTTPFunctionRoutes
28
+ routes: Map<HTTPMethod, Map<string, CoreHTTPFunctionRoute<any, any, any>>>
17
29
  meta: HTTPRoutesMeta
18
30
  }
19
31
  channel: {
20
- channels: CoreAPIChannels
32
+ channels: Map<string, CoreAPIChannel<any, any>>
21
33
  meta: ChannelsMeta
22
34
  }
23
35
  scheduler: {
@@ -32,14 +44,18 @@ interface PikkuState {
32
44
 
33
45
  export const resetPikkuState = () => {
34
46
  globalThis.pikkuState = {
47
+ functions: {
48
+ meta: {},
49
+ nameToFunction: new Map(),
50
+ },
35
51
  http: {
36
52
  middleware: [],
37
- routes: [],
53
+ routes: new Map(),
38
54
  meta: [],
39
55
  },
40
56
  channel: {
41
- channels: [],
42
- meta: [],
57
+ channels: new Map(),
58
+ meta: {},
43
59
  },
44
60
  scheduler: {
45
61
  tasks: new Map(),
@@ -5,10 +5,11 @@ import type {
5
5
  CreateSessionServices,
6
6
  } from '../types/core.types.js'
7
7
  import type { CoreScheduledTask } from './scheduler.types.js'
8
- import type { CoreAPIFunctionSessionless } from '../types/functions.types.js'
8
+ import type { CoreAPIFunctionSessionless } from '../function/functions.types.js'
9
9
  import { getErrorResponse } from '../errors/error-handler.js'
10
10
  import { closeSessionServices } from '../utils.js'
11
11
  import { pikkuState } from '../pikku-state.js'
12
+ import { addFunction, runPikkuFunc } from '../function/function-runner.js'
12
13
 
13
14
  export type RunScheduledTasksParams = {
14
15
  name: string
@@ -26,6 +27,13 @@ export const addScheduledTask = <
26
27
  >(
27
28
  scheduledTask: CoreScheduledTask<APIFunction>
28
29
  ) => {
30
+ const meta = pikkuState('scheduler', 'meta')
31
+ const taskMeta = meta[scheduledTask.name]
32
+ if (!taskMeta) {
33
+ throw new Error('Task metadata not found')
34
+ }
35
+ addFunction(taskMeta.pikkuFuncName, scheduledTask.func)
36
+
29
37
  const tasks = pikkuState('scheduler', 'tasks')
30
38
  if (tasks.has(scheduledTask.name)) {
31
39
  throw new Error(`Scheduled task already exists: ${scheduledTask.name}`)
@@ -51,33 +59,43 @@ export async function runScheduledTask<
51
59
  let sessionServices: CoreServices | undefined
52
60
  try {
53
61
  const task = pikkuState('scheduler', 'tasks').get(name)
54
-
62
+ const meta = pikkuState('scheduler', 'meta')[name]
55
63
  if (!task) {
56
64
  throw new ScheduledTaskNotFoundError(`Scheduled task not found: ${name}`)
57
65
  }
66
+ if (!meta) {
67
+ throw new ScheduledTaskNotFoundError(
68
+ `Scheduled task meta not found: ${name}`
69
+ )
70
+ }
58
71
 
59
72
  singletonServices.logger.info(
60
73
  `Running schedule task: ${name} | schedule: ${task.schedule}`
61
74
  )
62
75
 
63
- let allServices = singletonServices
64
- if (createSessionServices) {
65
- const sessionServices = await createSessionServices(
66
- singletonServices,
67
- {},
68
- session
69
- )
70
- allServices = { ...singletonServices, ...sessionServices }
76
+ const getAllServices = async () => {
77
+ if (createSessionServices) {
78
+ const sessionServices = await createSessionServices(
79
+ singletonServices,
80
+ {},
81
+ session
82
+ )
83
+ return { ...singletonServices, ...sessionServices }
84
+ }
85
+ return singletonServices
71
86
  }
72
87
 
73
- await task.func(allServices, undefined, session!)
88
+ return await runPikkuFunc(meta.pikkuFuncName, {
89
+ singletonServices,
90
+ getAllServices,
91
+ session,
92
+ data: undefined,
93
+ })
74
94
  } catch (e: any) {
75
95
  const errorResponse = getErrorResponse(e)
76
-
77
96
  if (errorResponse != null) {
78
97
  singletonServices.logger.error(e)
79
98
  }
80
-
81
99
  throw e
82
100
  } finally {
83
101
  if (sessionServices) {
@@ -1,17 +1,21 @@
1
1
  import { APIDocs, CoreUserSession } from '../types/core.types.js'
2
- import { CoreAPIFunctionSessionless } from '../types/functions.types.js'
2
+ import { CoreAPIFunctionSessionless } from '../function/functions.types.js'
3
3
 
4
4
  /**
5
5
  * Represents metadata for scheduled tasks, including title, schedule, and documentation.
6
6
  */
7
7
  export type ScheduledTasksMeta<UserSession extends CoreUserSession = any> =
8
- Array<{
9
- name: string
10
- schedule: string
11
- session?: UserSession
12
- docs?: APIDocs
13
- tags?: string[]
14
- }>
8
+ Record<
9
+ string,
10
+ {
11
+ pikkuFuncName: string
12
+ name: string
13
+ schedule: string
14
+ session?: UserSession
15
+ docs?: APIDocs
16
+ tags?: string[]
17
+ }
18
+ >
15
19
 
16
20
  /**
17
21
  * Represents a core scheduled task.