@pikku/core 0.7.2 → 0.7.4

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 (38) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/channel/channel-handler.js +0 -1
  3. package/dist/channel/channel-runner.js +16 -4
  4. package/dist/channel/local/local-channel-runner.js +3 -2
  5. package/dist/function/function-runner.d.ts +4 -5
  6. package/dist/function/function-runner.js +53 -13
  7. package/dist/function/functions.types.d.ts +5 -0
  8. package/dist/http/http-runner.js +8 -4
  9. package/dist/http/http.types.d.ts +0 -2
  10. package/dist/pikku-state.d.ts +7 -3
  11. package/dist/pikku-state.js +5 -2
  12. package/dist/rpc/index.d.ts +1 -0
  13. package/dist/rpc/index.js +1 -0
  14. package/dist/rpc/rpc-runner.d.ts +31 -0
  15. package/dist/rpc/rpc-runner.js +101 -0
  16. package/dist/rpc/rpc-types.d.ts +9 -0
  17. package/dist/rpc/rpc-types.js +1 -0
  18. package/dist/scheduler/scheduler-runner.js +8 -3
  19. package/dist/schema.js +4 -2
  20. package/dist/types/core.types.d.ts +2 -0
  21. package/lcov.info +722 -445
  22. package/package.json +2 -1
  23. package/src/channel/channel-handler.ts +0 -1
  24. package/src/channel/channel-runner.ts +18 -8
  25. package/src/channel/local/local-channel-runner.ts +3 -2
  26. package/src/function/function-runner.ts +81 -24
  27. package/src/function/functions.types.ts +11 -0
  28. package/src/http/http-runner.test.ts +3 -3
  29. package/src/http/http-runner.ts +9 -4
  30. package/src/http/http.types.ts +0 -2
  31. package/src/pikku-state.ts +14 -13
  32. package/src/rpc/index.ts +1 -0
  33. package/src/rpc/rpc-runner.ts +149 -0
  34. package/src/rpc/rpc-types.ts +10 -0
  35. package/src/scheduler/scheduler-runner.ts +8 -3
  36. package/src/schema.ts +4 -2
  37. package/src/types/core.types.ts +2 -0
  38. package/tsconfig.tsbuildinfo +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/core",
3
- "version": "0.7.2",
3
+ "version": "0.7.4",
4
4
  "author": "yasser.fadl@gmail.com",
5
5
  "license": "MIT",
6
6
  "module": "dist/index.js",
@@ -24,6 +24,7 @@
24
24
  "./channel/local": "./dist/channel/local/index.js",
25
25
  "./channel/serverless": "./dist/channel/serverless/index.js",
26
26
  "./http": "./dist/http/index.js",
27
+ "./rpc": "./dist/rpc/index.js",
27
28
  "./errors": "./dist/errors/index.js",
28
29
  "./scheduler": "./dist/scheduler/index.js",
29
30
  "./services": "./dist/services/index.js",
@@ -96,7 +96,6 @@ export const processMessageHandlers = (
96
96
  typeof onMessage === 'function' ? {} : onMessage.permissions
97
97
 
98
98
  return await runPikkuFunc(pikkuFuncName, {
99
- singletonServices: services,
100
99
  getAllServices: () => ({
101
100
  ...services,
102
101
  channel: channelHandler.getChannel(),
@@ -35,15 +35,16 @@ export const addChannel = <
35
35
 
36
36
  // Register onConnect function if provided
37
37
  if (channel.onConnect && channelMeta.connectPikkuFuncName) {
38
- addFunction(channelMeta.connectPikkuFuncName, channel.onConnect as any)
38
+ addFunction(channelMeta.connectPikkuFuncName, {
39
+ func: channel.onConnect as any,
40
+ })
39
41
  }
40
42
 
41
43
  // Register onDisconnect function if provided
42
44
  if (channel.onDisconnect && channelMeta.disconnectPikkuFuncName) {
43
- addFunction(
44
- channelMeta.disconnectPikkuFuncName,
45
- channel.onDisconnect as any
46
- )
45
+ addFunction(channelMeta.disconnectPikkuFuncName, {
46
+ func: channel.onDisconnect,
47
+ })
47
48
  }
48
49
 
49
50
  // Register onMessage function if provided
@@ -52,7 +53,9 @@ export const addChannel = <
52
53
  typeof channel.onMessage === 'function'
53
54
  ? channel.onMessage
54
55
  : channel.onMessage.func
55
- addFunction(channelMeta.message.pikkuFuncName, messageFunc as any)
56
+ addFunction(channelMeta.message.pikkuFuncName, {
57
+ func: messageFunc,
58
+ })
56
59
  }
57
60
 
58
61
  // Register functions in onMessageRoute
@@ -68,10 +71,17 @@ export const addChannel = <
68
71
  if (!routeMeta) return
69
72
 
70
73
  // Extract the function from the handler
71
- const routeFunc = typeof handler === 'function' ? handler : handler.func
74
+ const routeFunc =
75
+ typeof handler === 'function'
76
+ ? { func: handler }
77
+ : {
78
+ func: handler.func,
79
+ auth: handler.auth,
80
+ permissions: handler.permissions,
81
+ }
72
82
 
73
83
  // Register the function using the pikku name from metadata
74
- addFunction(routeMeta.pikkuFuncName, routeFunc as any)
84
+ addFunction(routeMeta.pikkuFuncName, routeFunc)
75
85
  })
76
86
  })
77
87
  }
@@ -14,6 +14,7 @@ import { runMiddleware } from '../../middleware-runner.js'
14
14
  import { PikkuUserSessionService } from '../../services/user-session-service.js'
15
15
  import { PikkuHTTP } from '../../http/http.types.js'
16
16
  import { runPikkuFuncDirectly } from '../../function/function-runner.js'
17
+ import { rpcService } from '../../rpc/rpc-runner.js'
17
18
 
18
19
  export const runLocalChannel = async ({
19
20
  singletonServices,
@@ -71,11 +72,11 @@ export const runLocalChannel = async ({
71
72
  )
72
73
  }
73
74
 
74
- const allServices = {
75
+ const allServices = rpcService.injectRPCService({
75
76
  ...singletonServices,
76
77
  ...sessionServices,
77
78
  userSession: userSession,
78
- }
79
+ })
79
80
 
80
81
  channelHandler.registerOnOpen(() => {
81
82
  if (channelConfig.onConnect && meta.connectPikkuFuncName) {
@@ -2,22 +2,68 @@ import { ForbiddenError } from '../errors/errors.js'
2
2
  import { verifyPermissions } from '../permissions.js'
3
3
  import { pikkuState } from '../pikku-state.js'
4
4
  import { coerceTopLevelDataFromSchema, validateSchema } from '../schema.js'
5
+ import { Logger } from '../services/logger.js'
6
+ import { CoreServices, CoreUserSession } from '../types/core.types.js'
5
7
  import {
6
- CoreServices,
7
- CoreSingletonServices,
8
- CoreUserSession,
9
- } from '../types/core.types.js'
10
- import {
11
- CoreAPIFunction,
12
- CoreAPIFunctionSessionless,
13
8
  CorePermissionGroup,
9
+ CorePikkuFunctionConfig,
14
10
  } from './functions.types.js'
15
11
 
12
+ // Permission merging function
13
+ function mergePermissions(
14
+ logger: Logger,
15
+ funcName: string,
16
+ funcPermissions?: CorePermissionGroup,
17
+ transportPermissions?: CorePermissionGroup
18
+ ): CorePermissionGroup | undefined {
19
+ if (!funcPermissions && !transportPermissions) {
20
+ return undefined
21
+ }
22
+
23
+ if (!funcPermissions) {
24
+ return transportPermissions
25
+ }
26
+
27
+ if (!transportPermissions) {
28
+ return funcPermissions
29
+ }
30
+
31
+ // Start with a copy of function permissions
32
+ const mergedPermissions = { ...funcPermissions }
33
+
34
+ // Merge in transport permissions
35
+ for (const [key, transportValue] of Object.entries(transportPermissions)) {
36
+ if (key in mergedPermissions) {
37
+ // For permission arrays, concatenate and deduplicate values
38
+ if (
39
+ Array.isArray(mergedPermissions[key]) &&
40
+ Array.isArray(transportValue)
41
+ ) {
42
+ mergedPermissions[key] = [
43
+ ...new Set([...mergedPermissions[key], ...transportValue]),
44
+ ]
45
+ }
46
+ // For other types, warn about conflict and use the more restrictive (transport-level) value
47
+ else {
48
+ logger.warn(
49
+ `Permission conflict on key "${key}" for function "${funcName}". Using transport-level permission.`
50
+ )
51
+ mergedPermissions[key] = transportValue
52
+ }
53
+ } else {
54
+ // If key doesn't exist in function permissions, add it
55
+ mergedPermissions[key] = transportValue
56
+ }
57
+ }
58
+
59
+ return mergedPermissions
60
+ }
61
+
16
62
  export const addFunction = (
17
63
  funcName: string,
18
- func: CoreAPIFunction<any, any> | CoreAPIFunctionSessionless<any, any>
64
+ funcConfig: CorePikkuFunctionConfig<any, any>
19
65
  ) => {
20
- pikkuState('functions', 'nameToFunction').set(funcName, func)
66
+ pikkuState('function', 'functions').set(funcName, funcConfig)
21
67
  }
22
68
 
23
69
  export const runPikkuFuncDirectly = async <In, Out>(
@@ -26,24 +72,22 @@ export const runPikkuFuncDirectly = async <In, Out>(
26
72
  data: In,
27
73
  session?: CoreUserSession
28
74
  ) => {
29
- const func = pikkuState('functions', 'nameToFunction').get(funcName)
30
- if (!func) {
75
+ const funcConfig = pikkuState('function', 'functions').get(funcName)
76
+ if (!funcConfig) {
31
77
  throw new Error(`Function not found: ${funcName}`)
32
78
  }
33
- return (await func(allServices, data, session!)) as Out
79
+ return (await funcConfig.func(allServices, data, session!)) as Out
34
80
  }
35
81
 
36
82
  export const runPikkuFunc = async <In = any, Out = any>(
37
83
  funcName: string,
38
84
  {
39
- singletonServices,
40
85
  getAllServices,
41
86
  data,
42
87
  session,
43
- permissions,
88
+ permissions: transportPermissions,
44
89
  coerceDataFromSchema,
45
90
  }: {
46
- singletonServices: CoreSingletonServices
47
91
  getAllServices: () => Promise<CoreServices> | CoreServices
48
92
  data: In
49
93
  session?: CoreUserSession
@@ -51,20 +95,29 @@ export const runPikkuFunc = async <In = any, Out = any>(
51
95
  coerceDataFromSchema?: boolean
52
96
  }
53
97
  ): Promise<Out> => {
54
- const func = pikkuState('functions', 'nameToFunction').get(funcName)
55
- if (!func) {
98
+ const funcConfig = pikkuState('function', 'functions').get(funcName)
99
+ if (!funcConfig) {
56
100
  throw new Error(`Function not found: ${funcName}`)
57
101
  }
58
- const funcMeta = pikkuState('functions', 'meta')[funcName]
102
+ const funcMeta = pikkuState('function', 'meta')[funcName]
59
103
  if (!funcMeta) {
60
104
  throw new Error(`Function meta not found: ${funcName}`)
61
105
  }
106
+
107
+ if (funcConfig.auth && !session) {
108
+ throw new ForbiddenError(
109
+ `Function ${funcName} requires authentication even though transport does not`
110
+ )
111
+ }
112
+
113
+ const allServices = await getAllServices()
114
+
62
115
  const schemaName = funcMeta.schemaName
63
116
  if (schemaName) {
64
117
  // Validate request data against the defined schema, if any
65
118
  await validateSchema(
66
- singletonServices.logger,
67
- singletonServices.schema,
119
+ allServices.logger,
120
+ allServices.schema,
68
121
  schemaName,
69
122
  data
70
123
  )
@@ -74,11 +127,15 @@ export const runPikkuFunc = async <In = any, Out = any>(
74
127
  }
75
128
  }
76
129
 
77
- const allServices = await getAllServices()
78
-
79
130
  // Execute permission checks
131
+ const mergedPermissions = mergePermissions(
132
+ allServices.logger,
133
+ funcName,
134
+ funcConfig.permissions,
135
+ transportPermissions
136
+ )
80
137
  const permissioned = await verifyPermissions(
81
- permissions,
138
+ mergedPermissions,
82
139
  allServices,
83
140
  data,
84
141
  session
@@ -88,5 +145,5 @@ export const runPikkuFunc = async <In = any, Out = any>(
88
145
  throw new ForbiddenError('Permission denied')
89
146
  }
90
147
 
91
- return (await func(allServices, data, session!)) as Out
148
+ return (await funcConfig.func(allServices, data, session!)) as Out
92
149
  }
@@ -75,3 +75,14 @@ export type CoreAPIPermission<
75
75
  export type CorePermissionGroup<APIPermission = CoreAPIPermission<any>> =
76
76
  | Record<string, APIPermission | APIPermission[]>
77
77
  | undefined
78
+
79
+ export type CorePikkuFunctionConfig<
80
+ APIFunction extends
81
+ | CoreAPIFunction<any, any>
82
+ | CoreAPIFunctionSessionless<any, any>,
83
+ APIPermission extends CoreAPIPermission<any> = CoreAPIPermission<any>,
84
+ > = {
85
+ func: APIFunction
86
+ auth?: boolean
87
+ permissions?: CorePermissionGroup<APIPermission>
88
+ }
@@ -15,8 +15,8 @@ const sessionMiddleware: PikkuMiddleware = async (services, _, next) => {
15
15
  await next()
16
16
  }
17
17
 
18
- const setHTTPFunctionMap = (fun: any) => {
19
- pikkuState('functions', 'meta', {
18
+ const setHTTPFunctionMap = (func: any) => {
19
+ pikkuState('function', 'meta', {
20
20
  pikku_func_name: {
21
21
  pikkuFuncName: 'pikku_func_name',
22
22
  services: ['userSession'],
@@ -31,7 +31,7 @@ const setHTTPFunctionMap = (fun: any) => {
31
31
  output: null,
32
32
  },
33
33
  ])
34
- addFunction('pikku_func_name', fun)
34
+ addFunction('pikku_func_name', { func })
35
35
  }
36
36
 
37
37
  describe('fetch', () => {
@@ -31,6 +31,7 @@ import { PikkuFetchHTTPResponse } from './pikku-fetch-http-response.js'
31
31
  import { PikkuFetchHTTPRequest } from './pikku-fetch-http-request.js'
32
32
  import { PikkuChannel } from '../channel/channel.types.js'
33
33
  import { addFunction, runPikkuFunc } from '../function/function-runner.js'
34
+ import { rpcService } from '../rpc/rpc-runner.js'
34
35
 
35
36
  /**
36
37
  * Registers middleware either globally or for a specific route.
@@ -101,7 +102,11 @@ export const addHTTPRoute = <
101
102
  if (!routeMeta) {
102
103
  throw new Error('Route metadata not found')
103
104
  }
104
- addFunction(routeMeta.pikkuFuncName, httpRoute.func as any)
105
+ addFunction(routeMeta.pikkuFuncName, {
106
+ func: httpRoute.func,
107
+ auth: httpRoute.auth,
108
+ permissions: httpRoute.permissions,
109
+ })
105
110
  const routes = pikkuState('http', 'routes')
106
111
  if (!routes.has(httpRoute.method)) {
107
112
  routes.set(httpRoute.method, new Map())
@@ -309,17 +314,17 @@ const executeRouteWithMiddleware = async (
309
314
  { http },
310
315
  session
311
316
  )
312
- return {
317
+
318
+ return rpcService.injectRPCService({
313
319
  ...singletonServices,
314
320
  ...sessionServices,
315
321
  http,
316
322
  userSession,
317
323
  channel,
318
- }
324
+ })
319
325
  }
320
326
 
321
327
  const result = await runPikkuFunc(meta.pikkuFuncName, {
322
- singletonServices,
323
328
  getAllServices,
324
329
  session,
325
330
  data,
@@ -196,8 +196,6 @@ export type HTTPRouteMeta = {
196
196
  method: HTTPMethod
197
197
  params?: string[]
198
198
  query?: string[]
199
- input: string | null
200
- output: string | null
201
199
  inputTypes?: HTTPFunctionMetaInputTypes
202
200
  docs?: APIDocs
203
201
  tags?: string[]
@@ -10,18 +10,16 @@ import {
10
10
  ScheduledTasksMeta,
11
11
  } from './scheduler/scheduler.types.js'
12
12
  import { ErrorDetails, PikkuError } from './errors/error-handler.js'
13
- import {
14
- CoreAPIFunction,
15
- CoreAPIFunctionSessionless,
16
- } from './function/functions.types.js'
13
+ import { CorePikkuFunctionConfig } from './function/functions.types.js'
14
+ import { RPCMeta } from './rpc/rpc-types.js'
17
15
 
18
16
  interface PikkuState {
19
- functions: {
17
+ function: {
20
18
  meta: FunctionsMeta
21
- nameToFunction: Map<
22
- string,
23
- CoreAPIFunction<any, any> | CoreAPIFunctionSessionless<any, any>
24
- >
19
+ functions: Map<string, CorePikkuFunctionConfig<any, any>>
20
+ }
21
+ rpc: {
22
+ meta: Record<string, RPCMeta>
25
23
  }
26
24
  http: {
27
25
  middleware: Array<{ route: string; middleware: PikkuMiddleware[] }>
@@ -44,9 +42,12 @@ interface PikkuState {
44
42
 
45
43
  export const resetPikkuState = () => {
46
44
  globalThis.pikkuState = {
47
- functions: {
45
+ function: {
46
+ meta: {},
47
+ functions: new Map(),
48
+ },
49
+ rpc: {
48
50
  meta: {},
49
- nameToFunction: new Map(),
50
51
  },
51
52
  http: {
52
53
  middleware: [],
@@ -59,13 +60,13 @@ export const resetPikkuState = () => {
59
60
  },
60
61
  scheduler: {
61
62
  tasks: new Map(),
62
- meta: [],
63
+ meta: [] as unknown as ScheduledTasksMeta,
63
64
  },
64
65
  misc: {
65
66
  errors: globalThis.pikkuState?.misc?.errors || new Map(),
66
67
  schemas: globalThis.pikkuState?.misc?.schema || new Map(),
67
68
  },
68
- }
69
+ } as PikkuState
69
70
  }
70
71
 
71
72
  if (!globalThis.pikkuState) {
@@ -0,0 +1 @@
1
+ export { initialize } from './rpc-runner.js'
@@ -0,0 +1,149 @@
1
+ import {
2
+ CoreSingletonServices,
3
+ CoreServices,
4
+ CreateSessionServices,
5
+ CoreUserSession,
6
+ } from '../types/core.types.js'
7
+ import { runPikkuFunc } from '../function/function-runner.js'
8
+ import { pikkuState } from '../pikku-state.js'
9
+ import { RPCMeta } from './rpc-types.js'
10
+ import { PikkuUserSessionService } from '../services/user-session-service.js'
11
+
12
+ // Type for the RPC service configuration
13
+ type RPCServiceConfig = {
14
+ singletonServices: CoreSingletonServices
15
+ createSessionServices: CreateSessionServices
16
+ coerceDataFromSchema: boolean
17
+ }
18
+
19
+ const getRPCMeta = (rpcName: string): RPCMeta => {
20
+ const rpc = pikkuState('rpc', 'meta')
21
+ const rpcMeta = rpc[rpcName]
22
+ if (!rpcMeta) {
23
+ throw new Error(`RPC function not found: ${rpcName}`)
24
+ }
25
+ return rpcMeta
26
+ }
27
+
28
+ // Context-aware RPC client for use within services
29
+ class ServiceRPC {
30
+ constructor(
31
+ private services: CoreServices,
32
+ private options: {
33
+ coerceDataFromSchema?: boolean
34
+ }
35
+ ) {}
36
+
37
+ public async rpc<In = any, Out = any>(
38
+ funcName: string,
39
+ data: In
40
+ ): Promise<Out> {
41
+ const session = await this.services.userSession?.get()
42
+ const rpcDepth = this.services.rpc?.depth || 0
43
+ const rpcMeta = getRPCMeta(funcName)
44
+ return runPikkuFunc<In, Out>(rpcMeta.pikkuFuncName, {
45
+ getAllServices: () => {
46
+ this.services.rpc = this.services.rpc
47
+ ? {
48
+ ...this.services.rpc,
49
+ depth: rpcDepth + 1,
50
+ global: false,
51
+ }
52
+ : undefined
53
+ return this.services
54
+ },
55
+ data,
56
+ session,
57
+ coerceDataFromSchema: this.options.coerceDataFromSchema,
58
+ })
59
+ }
60
+ }
61
+
62
+ // RPC Service class for the global interface
63
+ export class PikkuRPCService {
64
+ private config?: RPCServiceConfig
65
+
66
+ // Initialize the RPC service with configuration
67
+ initialize(config: RPCServiceConfig) {
68
+ this.config = config
69
+ }
70
+
71
+ // Convenience function for initializing
72
+ injectRPCService(coreServices: CoreServices, depth: number = 0) {
73
+ const serviceCopy = {
74
+ ...coreServices,
75
+ }
76
+ const serviceRPC = new ServiceRPC(serviceCopy, {
77
+ coerceDataFromSchema: this.config?.coerceDataFromSchema,
78
+ })
79
+ serviceCopy.rpc = {
80
+ depth,
81
+ global: false,
82
+ invoke: serviceRPC.rpc.bind(serviceRPC),
83
+ }
84
+ return serviceCopy
85
+ }
86
+
87
+ // Global RPC method to call functions by name
88
+ async rpc<In = any, Out = any>(
89
+ funcName: string,
90
+ data: In,
91
+ session?: CoreUserSession
92
+ ): Promise<Out> {
93
+ if (!this.config) {
94
+ throw new Error('RPC service not initialized')
95
+ }
96
+
97
+ const rpcMeta = getRPCMeta(funcName)
98
+ const { singletonServices, createSessionServices, coerceDataFromSchema } =
99
+ this.config
100
+
101
+ // Define the getAllServices function for runPikkuFunc
102
+ const getAllServices = async () => {
103
+ const userSession = new PikkuUserSessionService()
104
+ if (session) {
105
+ userSession.set(session)
106
+ }
107
+
108
+ const sessionServices = await createSessionServices(
109
+ singletonServices,
110
+ {},
111
+ session
112
+ )
113
+
114
+ return this.injectRPCService(
115
+ {
116
+ ...singletonServices,
117
+ ...sessionServices,
118
+ userSession,
119
+ },
120
+ 0
121
+ )
122
+ }
123
+
124
+ // Call the function using runPikkuFunc - it will handle permission merging
125
+ return runPikkuFunc<In, Out>(rpcMeta.pikkuFuncName, {
126
+ getAllServices,
127
+ data,
128
+ session,
129
+ coerceDataFromSchema: coerceDataFromSchema ?? true,
130
+ })
131
+ }
132
+ }
133
+
134
+ // Create a singleton instance
135
+ export const rpcService = new PikkuRPCService()
136
+
137
+ // Convenience function for initializing
138
+ export const initialize = (config: RPCServiceConfig) => {
139
+ rpcService.initialize(config)
140
+ }
141
+
142
+ // Convenience function for making standalone RPC calls
143
+ export const pikkuRPC = <In = any, Out = any, session = CoreUserSession>(
144
+ funcName: string,
145
+ data: In,
146
+ session?: CoreUserSession
147
+ ): Promise<Out> => {
148
+ return rpcService.rpc(funcName, data, session)
149
+ }
@@ -0,0 +1,10 @@
1
+ export type PikkuRPC = {
2
+ depth: number
3
+ global: boolean
4
+ invoke: any
5
+ }
6
+
7
+ export type RPCMeta = {
8
+ pikkuFuncName: string
9
+ exposed: boolean
10
+ }
@@ -10,6 +10,7 @@ import { getErrorResponse } from '../errors/error-handler.js'
10
10
  import { closeSessionServices } from '../utils.js'
11
11
  import { pikkuState } from '../pikku-state.js'
12
12
  import { addFunction, runPikkuFunc } from '../function/function-runner.js'
13
+ import { rpcService } from '../rpc/rpc-runner.js'
13
14
 
14
15
  export type RunScheduledTasksParams = {
15
16
  name: string
@@ -32,7 +33,9 @@ export const addScheduledTask = <
32
33
  if (!taskMeta) {
33
34
  throw new Error('Task metadata not found')
34
35
  }
35
- addFunction(taskMeta.pikkuFuncName, scheduledTask.func)
36
+ addFunction(taskMeta.pikkuFuncName, {
37
+ func: scheduledTask.func,
38
+ })
36
39
 
37
40
  const tasks = pikkuState('scheduler', 'tasks')
38
41
  if (tasks.has(scheduledTask.name)) {
@@ -80,13 +83,15 @@ export async function runScheduledTask<
80
83
  {},
81
84
  session
82
85
  )
83
- return { ...singletonServices, ...sessionServices }
86
+ return rpcService.injectRPCService({
87
+ ...singletonServices,
88
+ ...sessionServices,
89
+ })
84
90
  }
85
91
  return singletonServices
86
92
  }
87
93
 
88
94
  return await runPikkuFunc(meta.pikkuFuncName, {
89
- singletonServices,
90
95
  getAllServices,
91
96
  session,
92
97
  data: undefined,
package/src/schema.ts CHANGED
@@ -41,10 +41,12 @@ const validateAllSchemasLoaded = (
41
41
  const missingSchemas: string[] = []
42
42
 
43
43
  for (const route of routesMeta) {
44
- if (!route.input || validators.has(route.input)) {
44
+ const inputs = pikkuState('function', 'meta')[route.pikkuFuncName]?.inputs
45
+ const input = inputs?.[0]
46
+ if (!input || validators.has(input)) {
45
47
  continue
46
48
  }
47
- missingSchemas.push(route.input)
49
+ missingSchemas.push(input)
48
50
  }
49
51
 
50
52
  if (missingSchemas.length > 0) {
@@ -7,6 +7,7 @@ import { UserSessionService } from '../services/user-session-service.js'
7
7
  import { JWTService } from '../services/jwt-service.js'
8
8
  import { SecretService } from '../services/secret-service.js'
9
9
  import { PikkuChannel } from '../channel/channel.types.js'
10
+ import { PikkuRPC } from '../rpc/rpc-types.js'
10
11
 
11
12
  export interface FunctionServicesMeta {
12
13
  optimized: boolean
@@ -126,6 +127,7 @@ export type CoreServices<
126
127
  CoreServices extends Record<string, unknown> = {},
127
128
  > = SingletonServices &
128
129
  PikkuInteraction & {
130
+ rpc?: PikkuRPC
129
131
  userSession?: UserSessionService<UserSession>
130
132
  channel?: PikkuChannel<unknown, unknown>
131
133
  } & CoreServices