@pikku/core 0.7.1 → 0.7.3
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.
- package/CHANGELOG.md +12 -0
- package/dist/channel/channel-handler.js +0 -1
- package/dist/channel/channel-runner.js +16 -4
- package/dist/channel/local/local-channel-runner.js +3 -2
- package/dist/function/function-runner.d.ts +4 -5
- package/dist/function/function-runner.js +59 -17
- package/dist/function/functions.types.d.ts +5 -0
- package/dist/http/http-runner.js +8 -4
- package/dist/pikku-state.d.ts +7 -3
- package/dist/pikku-state.js +5 -2
- package/dist/rpc/index.d.ts +1 -0
- package/dist/rpc/index.js +1 -0
- package/dist/rpc/rpc-runner.d.ts +31 -0
- package/dist/rpc/rpc-runner.js +101 -0
- package/dist/rpc/rpc-types.d.ts +9 -0
- package/dist/rpc/rpc-types.js +1 -0
- package/dist/scheduler/scheduler-runner.js +8 -3
- package/dist/types/core.types.d.ts +2 -0
- package/lcov.info +805 -519
- package/package.json +2 -1
- package/src/channel/channel-handler.ts +0 -1
- package/src/channel/channel-runner.ts +18 -8
- package/src/channel/local/local-channel-runner.ts +3 -2
- package/src/function/function-runner.ts +89 -31
- package/src/function/functions.types.ts +11 -0
- package/src/http/http-runner.test.ts +3 -3
- package/src/http/http-runner.ts +9 -4
- package/src/pikku-state.ts +14 -13
- package/src/rpc/index.ts +1 -0
- package/src/rpc/rpc-runner.ts +149 -0
- package/src/rpc/rpc-types.ts +10 -0
- package/src/scheduler/scheduler-runner.ts +8 -3
- package/src/types/core.types.ts +2 -0
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
## 0.7.0
|
|
2
2
|
|
|
3
|
+
## 0.7.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 534fdef: feat: adding rpc (locally for now)
|
|
8
|
+
|
|
9
|
+
## 0.7.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- bb59874: fix: only try validating schemas if they exist in function runner
|
|
14
|
+
|
|
3
15
|
## 0.7.1
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -47,7 +47,6 @@ export const processMessageHandlers = (services, session, channelConfig, channel
|
|
|
47
47
|
const { pikkuFuncName } = getRouteMeta(channelConfig.name, routingProperty, routerValue);
|
|
48
48
|
const permissions = typeof onMessage === 'function' ? {} : onMessage.permissions;
|
|
49
49
|
return await runPikkuFunc(pikkuFuncName, {
|
|
50
|
-
singletonServices: services,
|
|
51
50
|
getAllServices: () => ({
|
|
52
51
|
...services,
|
|
53
52
|
channel: channelHandler.getChannel(),
|
|
@@ -17,18 +17,24 @@ export const addChannel = (channel) => {
|
|
|
17
17
|
pikkuState('channel', 'channels').set(channel.name, channel);
|
|
18
18
|
// Register onConnect function if provided
|
|
19
19
|
if (channel.onConnect && channelMeta.connectPikkuFuncName) {
|
|
20
|
-
addFunction(channelMeta.connectPikkuFuncName,
|
|
20
|
+
addFunction(channelMeta.connectPikkuFuncName, {
|
|
21
|
+
func: channel.onConnect,
|
|
22
|
+
});
|
|
21
23
|
}
|
|
22
24
|
// Register onDisconnect function if provided
|
|
23
25
|
if (channel.onDisconnect && channelMeta.disconnectPikkuFuncName) {
|
|
24
|
-
addFunction(channelMeta.disconnectPikkuFuncName,
|
|
26
|
+
addFunction(channelMeta.disconnectPikkuFuncName, {
|
|
27
|
+
func: channel.onDisconnect,
|
|
28
|
+
});
|
|
25
29
|
}
|
|
26
30
|
// Register onMessage function if provided
|
|
27
31
|
if (channel.onMessage && channelMeta.message?.pikkuFuncName) {
|
|
28
32
|
const messageFunc = typeof channel.onMessage === 'function'
|
|
29
33
|
? channel.onMessage
|
|
30
34
|
: channel.onMessage.func;
|
|
31
|
-
addFunction(channelMeta.message.pikkuFuncName,
|
|
35
|
+
addFunction(channelMeta.message.pikkuFuncName, {
|
|
36
|
+
func: messageFunc,
|
|
37
|
+
});
|
|
32
38
|
}
|
|
33
39
|
// Register functions in onMessageRoute
|
|
34
40
|
if (channel.onMessageRoute && channelMeta.messageRoutes) {
|
|
@@ -43,7 +49,13 @@ export const addChannel = (channel) => {
|
|
|
43
49
|
if (!routeMeta)
|
|
44
50
|
return;
|
|
45
51
|
// Extract the function from the handler
|
|
46
|
-
const routeFunc = typeof handler === 'function'
|
|
52
|
+
const routeFunc = typeof handler === 'function'
|
|
53
|
+
? { func: handler }
|
|
54
|
+
: {
|
|
55
|
+
func: handler.func,
|
|
56
|
+
auth: handler.auth,
|
|
57
|
+
permissions: handler.permissions,
|
|
58
|
+
};
|
|
47
59
|
// Register the function using the pikku name from metadata
|
|
48
60
|
addFunction(routeMeta.pikkuFuncName, routeFunc);
|
|
49
61
|
});
|
|
@@ -7,6 +7,7 @@ import { handleError } from '../../handle-error.js';
|
|
|
7
7
|
import { runMiddleware } from '../../middleware-runner.js';
|
|
8
8
|
import { PikkuUserSessionService } from '../../services/user-session-service.js';
|
|
9
9
|
import { runPikkuFuncDirectly } from '../../function/function-runner.js';
|
|
10
|
+
import { rpcService } from '../../rpc/rpc-runner.js';
|
|
10
11
|
export const runLocalChannel = async ({ singletonServices, channelId, request, response, route, createSessionServices, skipUserSession = false, respondWith404 = true, coerceDataFromSchema = true, logWarningsForStatusCodes = [], bubbleErrors = false, }) => {
|
|
11
12
|
let sessionServices;
|
|
12
13
|
let channelHandler;
|
|
@@ -36,11 +37,11 @@ export const runLocalChannel = async ({ singletonServices, channelId, request, r
|
|
|
36
37
|
if (createSessionServices) {
|
|
37
38
|
sessionServices = await createSessionServices(singletonServices, { http }, session);
|
|
38
39
|
}
|
|
39
|
-
const allServices = {
|
|
40
|
+
const allServices = rpcService.injectRPCService({
|
|
40
41
|
...singletonServices,
|
|
41
42
|
...sessionServices,
|
|
42
43
|
userSession: userSession,
|
|
43
|
-
};
|
|
44
|
+
});
|
|
44
45
|
channelHandler.registerOnOpen(() => {
|
|
45
46
|
if (channelConfig.onConnect && meta.connectPikkuFuncName) {
|
|
46
47
|
runPikkuFuncDirectly(meta.connectPikkuFuncName, { ...allServices, channel }, openingData);
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { CoreServices,
|
|
2
|
-
import {
|
|
3
|
-
export declare const addFunction: (funcName: string,
|
|
1
|
+
import { CoreServices, CoreUserSession } from '../types/core.types.js';
|
|
2
|
+
import { CorePermissionGroup, CorePikkuFunctionConfig } from './functions.types.js';
|
|
3
|
+
export declare const addFunction: (funcName: string, funcConfig: CorePikkuFunctionConfig<any, any>) => void;
|
|
4
4
|
export declare const runPikkuFuncDirectly: <In, Out>(funcName: string, allServices: CoreServices, data: In, session?: CoreUserSession) => Promise<Out>;
|
|
5
|
-
export declare const runPikkuFunc: <In = any, Out = any>(funcName: string, {
|
|
6
|
-
singletonServices: CoreSingletonServices;
|
|
5
|
+
export declare const runPikkuFunc: <In = any, Out = any>(funcName: string, { getAllServices, data, session, permissions: transportPermissions, coerceDataFromSchema, }: {
|
|
7
6
|
getAllServices: () => Promise<CoreServices> | CoreServices;
|
|
8
7
|
data: In;
|
|
9
8
|
session?: CoreUserSession;
|
|
@@ -2,37 +2,79 @@ 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
|
-
|
|
6
|
-
|
|
5
|
+
// Permission merging function
|
|
6
|
+
function mergePermissions(logger, funcName, funcPermissions, transportPermissions) {
|
|
7
|
+
if (!funcPermissions && !transportPermissions) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
if (!funcPermissions) {
|
|
11
|
+
return transportPermissions;
|
|
12
|
+
}
|
|
13
|
+
if (!transportPermissions) {
|
|
14
|
+
return funcPermissions;
|
|
15
|
+
}
|
|
16
|
+
// Start with a copy of function permissions
|
|
17
|
+
const mergedPermissions = { ...funcPermissions };
|
|
18
|
+
// Merge in transport permissions
|
|
19
|
+
for (const [key, transportValue] of Object.entries(transportPermissions)) {
|
|
20
|
+
if (key in mergedPermissions) {
|
|
21
|
+
// For permission arrays, concatenate and deduplicate values
|
|
22
|
+
if (Array.isArray(mergedPermissions[key]) &&
|
|
23
|
+
Array.isArray(transportValue)) {
|
|
24
|
+
mergedPermissions[key] = [
|
|
25
|
+
...new Set([...mergedPermissions[key], ...transportValue]),
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
// For other types, warn about conflict and use the more restrictive (transport-level) value
|
|
29
|
+
else {
|
|
30
|
+
logger.warn(`Permission conflict on key "${key}" for function "${funcName}". Using transport-level permission.`);
|
|
31
|
+
mergedPermissions[key] = transportValue;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
// If key doesn't exist in function permissions, add it
|
|
36
|
+
mergedPermissions[key] = transportValue;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return mergedPermissions;
|
|
40
|
+
}
|
|
41
|
+
export const addFunction = (funcName, funcConfig) => {
|
|
42
|
+
pikkuState('function', 'functions').set(funcName, funcConfig);
|
|
7
43
|
};
|
|
8
44
|
export const runPikkuFuncDirectly = async (funcName, allServices, data, session) => {
|
|
9
|
-
const
|
|
10
|
-
if (!
|
|
45
|
+
const funcConfig = pikkuState('function', 'functions').get(funcName);
|
|
46
|
+
if (!funcConfig) {
|
|
11
47
|
throw new Error(`Function not found: ${funcName}`);
|
|
12
48
|
}
|
|
13
|
-
return (await func(allServices, data, session));
|
|
49
|
+
return (await funcConfig.func(allServices, data, session));
|
|
14
50
|
};
|
|
15
|
-
export const runPikkuFunc = async (funcName, {
|
|
16
|
-
const
|
|
17
|
-
if (!
|
|
51
|
+
export const runPikkuFunc = async (funcName, { getAllServices, data, session, permissions: transportPermissions, coerceDataFromSchema, }) => {
|
|
52
|
+
const funcConfig = pikkuState('function', 'functions').get(funcName);
|
|
53
|
+
if (!funcConfig) {
|
|
18
54
|
throw new Error(`Function not found: ${funcName}`);
|
|
19
55
|
}
|
|
20
|
-
const funcMeta = pikkuState('
|
|
56
|
+
const funcMeta = pikkuState('function', 'meta')[funcName];
|
|
21
57
|
if (!funcMeta) {
|
|
22
58
|
throw new Error(`Function meta not found: ${funcName}`);
|
|
23
59
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
await validateSchema(singletonServices.logger, singletonServices.schema, schemaName, data);
|
|
27
|
-
// Coerce (top level) query string parameters or date objects if specified by the schema
|
|
28
|
-
if (coerceDataFromSchema && schemaName) {
|
|
29
|
-
coerceTopLevelDataFromSchema(schemaName, data);
|
|
60
|
+
if (funcConfig.auth && !session) {
|
|
61
|
+
throw new ForbiddenError(`Function ${funcName} requires authentication even though transport does not`);
|
|
30
62
|
}
|
|
31
63
|
const allServices = await getAllServices();
|
|
64
|
+
const schemaName = funcMeta.schemaName;
|
|
65
|
+
if (schemaName) {
|
|
66
|
+
// Validate request data against the defined schema, if any
|
|
67
|
+
await validateSchema(allServices.logger, allServices.schema, schemaName, data);
|
|
68
|
+
// Coerce (top level) query string parameters or date objects if specified by the schema
|
|
69
|
+
if (coerceDataFromSchema) {
|
|
70
|
+
coerceTopLevelDataFromSchema(schemaName, data);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
32
73
|
// Execute permission checks
|
|
33
|
-
const
|
|
74
|
+
const mergedPermissions = mergePermissions(allServices.logger, funcName, funcConfig.permissions, transportPermissions);
|
|
75
|
+
const permissioned = await verifyPermissions(mergedPermissions, allServices, data, session);
|
|
34
76
|
if (permissioned === false) {
|
|
35
77
|
throw new ForbiddenError('Permission denied');
|
|
36
78
|
}
|
|
37
|
-
return (await func(allServices, data, session));
|
|
79
|
+
return (await funcConfig.func(allServices, data, session));
|
|
38
80
|
};
|
|
@@ -35,3 +35,8 @@ export type CoreAPIFunctionSessionless<In, Out, ChannelData extends unknown | nu
|
|
|
35
35
|
*/
|
|
36
36
|
export type CoreAPIPermission<In = any, Services extends CoreSingletonServices = CoreServices, Session extends CoreUserSession = CoreUserSession> = (services: Services, data: In, session?: Session) => Promise<boolean>;
|
|
37
37
|
export type CorePermissionGroup<APIPermission = CoreAPIPermission<any>> = Record<string, APIPermission | APIPermission[]> | undefined;
|
|
38
|
+
export type CorePikkuFunctionConfig<APIFunction extends CoreAPIFunction<any, any> | CoreAPIFunctionSessionless<any, any>, APIPermission extends CoreAPIPermission<any> = CoreAPIPermission<any>> = {
|
|
39
|
+
func: APIFunction;
|
|
40
|
+
auth?: boolean;
|
|
41
|
+
permissions?: CorePermissionGroup<APIPermission>;
|
|
42
|
+
};
|
package/dist/http/http-runner.js
CHANGED
|
@@ -8,6 +8,7 @@ import { pikkuState } from '../pikku-state.js';
|
|
|
8
8
|
import { PikkuFetchHTTPResponse } from './pikku-fetch-http-response.js';
|
|
9
9
|
import { PikkuFetchHTTPRequest } from './pikku-fetch-http-request.js';
|
|
10
10
|
import { addFunction, runPikkuFunc } from '../function/function-runner.js';
|
|
11
|
+
import { rpcService } from '../rpc/rpc-runner.js';
|
|
11
12
|
/**
|
|
12
13
|
* Registers middleware either globally or for a specific route.
|
|
13
14
|
*
|
|
@@ -54,7 +55,11 @@ export const addHTTPRoute = (httpRoute) => {
|
|
|
54
55
|
if (!routeMeta) {
|
|
55
56
|
throw new Error('Route metadata not found');
|
|
56
57
|
}
|
|
57
|
-
addFunction(routeMeta.pikkuFuncName,
|
|
58
|
+
addFunction(routeMeta.pikkuFuncName, {
|
|
59
|
+
func: httpRoute.func,
|
|
60
|
+
auth: httpRoute.auth,
|
|
61
|
+
permissions: httpRoute.permissions,
|
|
62
|
+
});
|
|
58
63
|
const routes = pikkuState('http', 'routes');
|
|
59
64
|
if (!routes.has(httpRoute.method)) {
|
|
60
65
|
routes.set(httpRoute.method, new Map());
|
|
@@ -204,16 +209,15 @@ const executeRouteWithMiddleware = async (services, matchedRoute, http, options)
|
|
|
204
209
|
}
|
|
205
210
|
// Create session-specific services for handling the request
|
|
206
211
|
sessionServices = await createSessionServices({ ...singletonServices, userSession, channel }, { http }, session);
|
|
207
|
-
return {
|
|
212
|
+
return rpcService.injectRPCService({
|
|
208
213
|
...singletonServices,
|
|
209
214
|
...sessionServices,
|
|
210
215
|
http,
|
|
211
216
|
userSession,
|
|
212
217
|
channel,
|
|
213
|
-
};
|
|
218
|
+
});
|
|
214
219
|
};
|
|
215
220
|
const result = await runPikkuFunc(meta.pikkuFuncName, {
|
|
216
|
-
singletonServices,
|
|
217
221
|
getAllServices,
|
|
218
222
|
session,
|
|
219
223
|
data,
|
package/dist/pikku-state.d.ts
CHANGED
|
@@ -3,11 +3,15 @@ import { CoreHTTPFunctionRoute, HTTPMethod, HTTPRoutesMeta } from './http/http.t
|
|
|
3
3
|
import { FunctionsMeta, PikkuMiddleware } from './types/core.types.js';
|
|
4
4
|
import { CoreScheduledTask, ScheduledTasksMeta } from './scheduler/scheduler.types.js';
|
|
5
5
|
import { ErrorDetails, PikkuError } from './errors/error-handler.js';
|
|
6
|
-
import {
|
|
6
|
+
import { CorePikkuFunctionConfig } from './function/functions.types.js';
|
|
7
|
+
import { RPCMeta } from './rpc/rpc-types.js';
|
|
7
8
|
interface PikkuState {
|
|
8
|
-
|
|
9
|
+
function: {
|
|
9
10
|
meta: FunctionsMeta;
|
|
10
|
-
|
|
11
|
+
functions: Map<string, CorePikkuFunctionConfig<any, any>>;
|
|
12
|
+
};
|
|
13
|
+
rpc: {
|
|
14
|
+
meta: Record<string, RPCMeta>;
|
|
11
15
|
};
|
|
12
16
|
http: {
|
|
13
17
|
middleware: Array<{
|
package/dist/pikku-state.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { initialize } from './rpc-runner.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { initialize } from './rpc-runner.js';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { CoreSingletonServices, CoreServices, CreateSessionServices, CoreUserSession } from '../types/core.types.js';
|
|
2
|
+
type RPCServiceConfig = {
|
|
3
|
+
singletonServices: CoreSingletonServices;
|
|
4
|
+
createSessionServices: CreateSessionServices;
|
|
5
|
+
coerceDataFromSchema: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare class PikkuRPCService {
|
|
8
|
+
private config?;
|
|
9
|
+
initialize(config: RPCServiceConfig): void;
|
|
10
|
+
injectRPCService(coreServices: CoreServices, depth?: number): {
|
|
11
|
+
jwt?: import("../index.js").JWTService;
|
|
12
|
+
schema?: import("../index.js").SchemaService;
|
|
13
|
+
config: {
|
|
14
|
+
logLevel?: import("../index.js").LogLevel;
|
|
15
|
+
secrets?: {};
|
|
16
|
+
};
|
|
17
|
+
logger: import("../index.js").Logger;
|
|
18
|
+
variables: import("../index.js").VariablesService;
|
|
19
|
+
eventHub?: import("../index.js").EventHubService<unknown>;
|
|
20
|
+
secrets?: import("../index.js").SecretService;
|
|
21
|
+
http?: import("../index.js").PikkuHTTP;
|
|
22
|
+
rpc?: import("./rpc-types.js").PikkuRPC;
|
|
23
|
+
userSession?: import("../services/user-session-service.js").UserSessionService<CoreUserSession> | undefined;
|
|
24
|
+
channel?: import("../index.js").PikkuChannel<unknown, unknown>;
|
|
25
|
+
};
|
|
26
|
+
rpc<In = any, Out = any>(funcName: string, data: In, session?: CoreUserSession): Promise<Out>;
|
|
27
|
+
}
|
|
28
|
+
export declare const rpcService: PikkuRPCService;
|
|
29
|
+
export declare const initialize: (config: RPCServiceConfig) => void;
|
|
30
|
+
export declare const pikkuRPC: <In = any, Out = any, session = CoreUserSession>(funcName: string, data: In, session?: CoreUserSession) => Promise<Out>;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { runPikkuFunc } from '../function/function-runner.js';
|
|
2
|
+
import { pikkuState } from '../pikku-state.js';
|
|
3
|
+
import { PikkuUserSessionService } from '../services/user-session-service.js';
|
|
4
|
+
const getRPCMeta = (rpcName) => {
|
|
5
|
+
const rpc = pikkuState('rpc', 'meta');
|
|
6
|
+
const rpcMeta = rpc[rpcName];
|
|
7
|
+
if (!rpcMeta) {
|
|
8
|
+
throw new Error(`RPC function not found: ${rpcName}`);
|
|
9
|
+
}
|
|
10
|
+
return rpcMeta;
|
|
11
|
+
};
|
|
12
|
+
// Context-aware RPC client for use within services
|
|
13
|
+
class ServiceRPC {
|
|
14
|
+
services;
|
|
15
|
+
options;
|
|
16
|
+
constructor(services, options) {
|
|
17
|
+
this.services = services;
|
|
18
|
+
this.options = options;
|
|
19
|
+
}
|
|
20
|
+
async rpc(funcName, data) {
|
|
21
|
+
const session = await this.services.userSession?.get();
|
|
22
|
+
const rpcDepth = this.services.rpc?.depth || 0;
|
|
23
|
+
const rpcMeta = getRPCMeta(funcName);
|
|
24
|
+
return runPikkuFunc(rpcMeta.pikkuFuncName, {
|
|
25
|
+
getAllServices: () => {
|
|
26
|
+
this.services.rpc = this.services.rpc
|
|
27
|
+
? {
|
|
28
|
+
...this.services.rpc,
|
|
29
|
+
depth: rpcDepth + 1,
|
|
30
|
+
global: false,
|
|
31
|
+
}
|
|
32
|
+
: undefined;
|
|
33
|
+
return this.services;
|
|
34
|
+
},
|
|
35
|
+
data,
|
|
36
|
+
session,
|
|
37
|
+
coerceDataFromSchema: this.options.coerceDataFromSchema,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// RPC Service class for the global interface
|
|
42
|
+
export class PikkuRPCService {
|
|
43
|
+
config;
|
|
44
|
+
// Initialize the RPC service with configuration
|
|
45
|
+
initialize(config) {
|
|
46
|
+
this.config = config;
|
|
47
|
+
}
|
|
48
|
+
// Convenience function for initializing
|
|
49
|
+
injectRPCService(coreServices, depth = 0) {
|
|
50
|
+
const serviceCopy = {
|
|
51
|
+
...coreServices,
|
|
52
|
+
};
|
|
53
|
+
const serviceRPC = new ServiceRPC(serviceCopy, {
|
|
54
|
+
coerceDataFromSchema: this.config?.coerceDataFromSchema,
|
|
55
|
+
});
|
|
56
|
+
serviceCopy.rpc = {
|
|
57
|
+
depth,
|
|
58
|
+
global: false,
|
|
59
|
+
invoke: serviceRPC.rpc.bind(serviceRPC),
|
|
60
|
+
};
|
|
61
|
+
return serviceCopy;
|
|
62
|
+
}
|
|
63
|
+
// Global RPC method to call functions by name
|
|
64
|
+
async rpc(funcName, data, session) {
|
|
65
|
+
if (!this.config) {
|
|
66
|
+
throw new Error('RPC service not initialized');
|
|
67
|
+
}
|
|
68
|
+
const rpcMeta = getRPCMeta(funcName);
|
|
69
|
+
const { singletonServices, createSessionServices, coerceDataFromSchema } = this.config;
|
|
70
|
+
// Define the getAllServices function for runPikkuFunc
|
|
71
|
+
const getAllServices = async () => {
|
|
72
|
+
const userSession = new PikkuUserSessionService();
|
|
73
|
+
if (session) {
|
|
74
|
+
userSession.set(session);
|
|
75
|
+
}
|
|
76
|
+
const sessionServices = await createSessionServices(singletonServices, {}, session);
|
|
77
|
+
return this.injectRPCService({
|
|
78
|
+
...singletonServices,
|
|
79
|
+
...sessionServices,
|
|
80
|
+
userSession,
|
|
81
|
+
}, 0);
|
|
82
|
+
};
|
|
83
|
+
// Call the function using runPikkuFunc - it will handle permission merging
|
|
84
|
+
return runPikkuFunc(rpcMeta.pikkuFuncName, {
|
|
85
|
+
getAllServices,
|
|
86
|
+
data,
|
|
87
|
+
session,
|
|
88
|
+
coerceDataFromSchema: coerceDataFromSchema ?? true,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Create a singleton instance
|
|
93
|
+
export const rpcService = new PikkuRPCService();
|
|
94
|
+
// Convenience function for initializing
|
|
95
|
+
export const initialize = (config) => {
|
|
96
|
+
rpcService.initialize(config);
|
|
97
|
+
};
|
|
98
|
+
// Convenience function for making standalone RPC calls
|
|
99
|
+
export const pikkuRPC = (funcName, data, session) => {
|
|
100
|
+
return rpcService.rpc(funcName, data, session);
|
|
101
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -2,13 +2,16 @@ import { getErrorResponse } from '../errors/error-handler.js';
|
|
|
2
2
|
import { closeSessionServices } from '../utils.js';
|
|
3
3
|
import { pikkuState } from '../pikku-state.js';
|
|
4
4
|
import { addFunction, runPikkuFunc } from '../function/function-runner.js';
|
|
5
|
+
import { rpcService } from '../rpc/rpc-runner.js';
|
|
5
6
|
export const addScheduledTask = (scheduledTask) => {
|
|
6
7
|
const meta = pikkuState('scheduler', 'meta');
|
|
7
8
|
const taskMeta = meta[scheduledTask.name];
|
|
8
9
|
if (!taskMeta) {
|
|
9
10
|
throw new Error('Task metadata not found');
|
|
10
11
|
}
|
|
11
|
-
addFunction(taskMeta.pikkuFuncName,
|
|
12
|
+
addFunction(taskMeta.pikkuFuncName, {
|
|
13
|
+
func: scheduledTask.func,
|
|
14
|
+
});
|
|
12
15
|
const tasks = pikkuState('scheduler', 'tasks');
|
|
13
16
|
if (tasks.has(scheduledTask.name)) {
|
|
14
17
|
throw new Error(`Scheduled task already exists: ${scheduledTask.name}`);
|
|
@@ -35,12 +38,14 @@ export async function runScheduledTask({ name, session, singletonServices, creat
|
|
|
35
38
|
const getAllServices = async () => {
|
|
36
39
|
if (createSessionServices) {
|
|
37
40
|
const sessionServices = await createSessionServices(singletonServices, {}, session);
|
|
38
|
-
return {
|
|
41
|
+
return rpcService.injectRPCService({
|
|
42
|
+
...singletonServices,
|
|
43
|
+
...sessionServices,
|
|
44
|
+
});
|
|
39
45
|
}
|
|
40
46
|
return singletonServices;
|
|
41
47
|
};
|
|
42
48
|
return await runPikkuFunc(meta.pikkuFuncName, {
|
|
43
|
-
singletonServices,
|
|
44
49
|
getAllServices,
|
|
45
50
|
session,
|
|
46
51
|
data: undefined,
|
|
@@ -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
|
export interface FunctionServicesMeta {
|
|
11
12
|
optimized: boolean;
|
|
12
13
|
services: string[];
|
|
@@ -93,6 +94,7 @@ export type PikkuMiddleware<SingletonServices extends CoreSingletonServices = Co
|
|
|
93
94
|
* Represents the core services used by Pikku, including singleton services and the request/response interaction.
|
|
94
95
|
*/
|
|
95
96
|
export type CoreServices<SingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession, CoreServices extends Record<string, unknown> = {}> = SingletonServices & PikkuInteraction & {
|
|
97
|
+
rpc?: PikkuRPC;
|
|
96
98
|
userSession?: UserSessionService<UserSession>;
|
|
97
99
|
channel?: PikkuChannel<unknown, unknown>;
|
|
98
100
|
} & CoreServices;
|