@pikku/core 0.12.0 → 0.12.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.
- package/CHANGELOG.md +24 -0
- package/dist/errors/errors.d.ts +6 -0
- package/dist/errors/errors.js +10 -0
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.js +1 -0
- package/dist/function/function-runner.d.ts +3 -3
- package/dist/function/function-runner.js +66 -44
- package/dist/function/functions.types.d.ts +1 -0
- package/dist/handle-error.d.ts +2 -2
- package/dist/handle-error.js +2 -2
- package/dist/index.d.ts +5 -3
- package/dist/index.js +2 -1
- package/dist/internal.d.ts +3 -0
- package/dist/internal.js +2 -0
- package/dist/middleware/auth-bearer.d.ts +1 -1
- package/dist/middleware/auth-bearer.js +1 -1
- package/dist/middleware/auth-cookie.d.ts +2 -2
- package/dist/middleware/auth-cookie.js +1 -1
- package/dist/middleware/cors.d.ts +2 -2
- package/dist/middleware/cors.js +45 -37
- package/dist/middleware-runner.d.ts +1 -1
- package/dist/permissions.d.ts +2 -2
- package/dist/pikku-state.d.ts +9 -8
- package/dist/pikku-state.js +30 -17
- package/dist/schema.d.ts +4 -4
- package/dist/schema.js +9 -5
- package/dist/services/ai-agent-runner-service.d.ts +22 -3
- package/dist/services/ai-run-state-service.d.ts +1 -1
- package/dist/services/ai-storage-service.d.ts +1 -1
- package/dist/services/content-service.d.ts +6 -0
- package/dist/services/gopass-secrets.d.ts +1 -1
- package/dist/services/gopass-secrets.js +3 -0
- package/dist/services/in-memory-ai-run-state-service.d.ts +15 -0
- package/dist/services/in-memory-ai-run-state-service.js +44 -0
- package/dist/services/in-memory-trigger-service.d.ts +0 -1
- package/dist/services/in-memory-trigger-service.js +6 -8
- package/dist/services/index.d.ts +3 -2
- package/dist/services/index.js +1 -0
- package/dist/services/local-content.d.ts +2 -1
- package/dist/services/local-content.js +6 -1
- package/dist/services/local-secrets.d.ts +2 -2
- package/dist/services/local-variables.d.ts +1 -1
- package/dist/services/logger-console.d.ts +2 -1
- package/dist/services/scheduler-service.d.ts +0 -12
- package/dist/services/scheduler-service.js +0 -6
- package/dist/services/scoped-secret-service.d.ts +1 -1
- package/dist/services/trigger-service.d.ts +0 -7
- package/dist/services/user-session-service.d.ts +3 -3
- package/dist/services/workflow-service.d.ts +2 -3
- package/dist/types/core.types.d.ts +24 -18
- package/dist/types/state.types.d.ts +17 -15
- package/dist/utils.d.ts +5 -5
- package/dist/utils.js +12 -9
- package/dist/wirings/ai-agent/ai-agent-assistant-ui.d.ts +5 -0
- package/dist/wirings/ai-agent/ai-agent-assistant-ui.js +146 -0
- package/dist/wirings/ai-agent/ai-agent-helpers.d.ts +28 -0
- package/dist/wirings/ai-agent/ai-agent-helpers.js +40 -0
- package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +4 -5
- package/dist/wirings/ai-agent/ai-agent-prepare.js +66 -30
- package/dist/wirings/ai-agent/ai-agent-registry.d.ts +1 -1
- package/dist/wirings/ai-agent/ai-agent-runner.js +107 -8
- package/dist/wirings/ai-agent/ai-agent-stream.d.ts +2 -1
- package/dist/wirings/ai-agent/ai-agent-stream.js +246 -87
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +61 -2
- package/dist/wirings/ai-agent/index.d.ts +3 -1
- package/dist/wirings/ai-agent/index.js +2 -0
- package/dist/wirings/channel/channel-common.d.ts +2 -2
- package/dist/wirings/channel/channel-handler.d.ts +3 -3
- package/dist/wirings/channel/channel-handler.js +4 -4
- package/dist/wirings/channel/channel-runner.d.ts +5 -5
- package/dist/wirings/channel/channel-runner.js +3 -2
- package/dist/wirings/channel/channel-store.d.ts +1 -1
- package/dist/wirings/channel/channel.types.d.ts +4 -6
- package/dist/wirings/channel/local/local-channel-runner.d.ts +7 -2
- package/dist/wirings/channel/local/local-channel-runner.js +4 -3
- package/dist/wirings/channel/local/local-eventhub-service.d.ts +2 -2
- package/dist/wirings/channel/log-channels.d.ts +1 -1
- package/dist/wirings/channel/pikku-abstract-channel-handler.d.ts +1 -1
- package/dist/wirings/channel/serverless/serverless-channel-runner.d.ts +4 -4
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +25 -19
- package/dist/wirings/cli/channel/cli-channel-runner.d.ts +1 -1
- package/dist/wirings/cli/cli-runner.d.ts +2 -2
- package/dist/wirings/cli/cli.types.d.ts +3 -3
- package/dist/wirings/cli/command-parser.d.ts +1 -1
- package/dist/wirings/http/http-runner.d.ts +9 -9
- package/dist/wirings/http/http-runner.js +33 -14
- package/dist/wirings/http/http.types.d.ts +2 -6
- package/dist/wirings/http/log-http-routes.d.ts +1 -1
- package/dist/wirings/http/pikku-fetch-http-request.d.ts +1 -1
- package/dist/wirings/http/pikku-fetch-http-response.d.ts +2 -2
- package/dist/wirings/http/pikku-fetch-http-response.js +6 -1
- package/dist/wirings/http/routers/http-router.d.ts +1 -1
- package/dist/wirings/http/routers/path-to-regex.d.ts +2 -2
- package/dist/wirings/mcp/mcp-endpoint-registry.d.ts +1 -1
- package/dist/wirings/mcp/mcp-runner.d.ts +0 -3
- package/dist/wirings/mcp/mcp-runner.js +4 -2
- package/dist/wirings/mcp/mcp.types.d.ts +2 -2
- package/dist/wirings/oauth2/oauth2-client.d.ts +3 -3
- package/dist/wirings/oauth2/wire-oauth2-credential.d.ts +1 -1
- package/dist/wirings/queue/index.d.ts +1 -1
- package/dist/wirings/queue/index.js +1 -1
- package/dist/wirings/queue/queue-runner.d.ts +1 -12
- package/dist/wirings/queue/queue-runner.js +4 -12
- package/dist/wirings/queue/queue.types.d.ts +3 -9
- package/dist/wirings/queue/register-queue-helper.d.ts +2 -2
- package/dist/wirings/queue/register-queue-helper.js +1 -1
- package/dist/wirings/queue/validate-worker-config.d.ts +1 -1
- package/dist/wirings/rpc/index.d.ts +2 -0
- package/dist/wirings/rpc/index.js +1 -0
- package/dist/wirings/rpc/rpc-runner.d.ts +36 -14
- package/dist/wirings/rpc/rpc-runner.js +56 -28
- package/dist/wirings/rpc/rpc-types.d.ts +14 -2
- package/dist/wirings/rpc/wire-addon.d.ts +10 -0
- package/dist/wirings/rpc/wire-addon.js +9 -0
- package/dist/wirings/scheduler/index.d.ts +1 -1
- package/dist/wirings/scheduler/index.js +1 -1
- package/dist/wirings/scheduler/log-schedulers.d.ts +1 -1
- package/dist/wirings/scheduler/scheduler-runner.d.ts +3 -10
- package/dist/wirings/scheduler/scheduler-runner.js +4 -25
- package/dist/wirings/scheduler/scheduler.types.d.ts +2 -2
- package/dist/wirings/trigger/pikku-trigger-service.d.ts +2 -6
- package/dist/wirings/trigger/pikku-trigger-service.js +11 -16
- package/dist/wirings/trigger/trigger-runner.d.ts +1 -5
- package/dist/wirings/trigger/trigger-runner.js +4 -3
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +2 -6
- package/dist/wirings/workflow/pikku-workflow-service.js +20 -29
- package/dist/wirings/workflow/workflow.types.d.ts +2 -2
- package/package.json +2 -1
- package/src/crypto-utils.test.ts +116 -0
- package/src/errors/error.test.ts +143 -2
- package/src/errors/errors.ts +10 -0
- package/src/errors/index.ts +1 -0
- package/src/function/function-runner.test.ts +2 -2
- package/src/function/function-runner.ts +72 -49
- package/src/function/functions.types.ts +1 -0
- package/src/handle-error.test.ts +361 -0
- package/src/handle-error.ts +4 -4
- package/src/index.ts +3 -10
- package/src/internal.ts +6 -0
- package/src/middleware/auth-apikey.test.ts +1 -1
- package/src/middleware/auth-bearer.test.ts +1 -1
- package/src/middleware/auth-bearer.ts +2 -5
- package/src/middleware/auth-cookie.test.ts +1 -1
- package/src/middleware/auth-cookie.ts +3 -5
- package/src/middleware/cors.test.ts +424 -0
- package/src/middleware/cors.ts +14 -6
- package/src/middleware/remote-auth.test.ts +488 -0
- package/src/middleware-runner.test.ts +1 -1
- package/src/middleware-runner.ts +1 -1
- package/src/permissions.test.ts +2 -2
- package/src/permissions.ts +2 -2
- package/src/pikku-state.test.ts +224 -0
- package/src/pikku-state.ts +40 -28
- package/src/schema.test.ts +198 -6
- package/src/schema.ts +11 -7
- package/src/services/ai-agent-runner-service.ts +15 -3
- package/src/services/ai-run-state-service.ts +1 -1
- package/src/services/ai-storage-service.ts +1 -1
- package/src/services/content-service.ts +7 -0
- package/src/services/gopass-secrets.ts +4 -1
- package/src/services/in-memory-ai-run-state-service.ts +73 -0
- package/src/services/in-memory-trigger-service.ts +6 -10
- package/src/services/in-memory-workflow-service.test.ts +351 -0
- package/src/services/index.ts +2 -1
- package/src/services/local-content.ts +9 -3
- package/src/services/local-secrets.test.ts +80 -0
- package/src/services/local-secrets.ts +2 -2
- package/src/services/local-variables.test.ts +61 -0
- package/src/services/local-variables.ts +1 -1
- package/src/services/logger-console.test.ts +118 -0
- package/src/services/logger-console.ts +2 -1
- package/src/services/scheduler-service.ts +0 -18
- package/src/services/scoped-secret-service.test.ts +74 -0
- package/src/services/scoped-secret-service.ts +1 -1
- package/src/services/trigger-service.ts +0 -21
- package/src/services/typed-secret-service.test.ts +93 -0
- package/src/services/typed-variables-service.test.ts +73 -0
- package/src/services/user-session-service.test.ts +113 -0
- package/src/services/user-session-service.ts +3 -3
- package/src/services/workflow-service.ts +2 -12
- package/src/time-utils.test.ts +1 -1
- package/src/types/core.types.ts +25 -19
- package/src/types/state.types.ts +24 -15
- package/src/utils.test.ts +350 -0
- package/src/utils.ts +14 -13
- package/src/wirings/ai-agent/ai-agent-assistant-ui.test.ts +363 -0
- package/src/wirings/ai-agent/ai-agent-assistant-ui.ts +214 -0
- package/src/wirings/ai-agent/ai-agent-helpers.test.ts +152 -0
- package/src/wirings/ai-agent/ai-agent-helpers.ts +76 -0
- package/src/wirings/ai-agent/ai-agent-model-config.test.ts +115 -0
- package/src/wirings/ai-agent/ai-agent-prepare.ts +79 -45
- package/src/wirings/ai-agent/ai-agent-registry.ts +1 -1
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +245 -3
- package/src/wirings/ai-agent/ai-agent-runner.ts +121 -7
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +430 -24
- package/src/wirings/ai-agent/ai-agent-stream.ts +377 -102
- package/src/wirings/ai-agent/ai-agent.types.ts +57 -2
- package/src/wirings/ai-agent/index.ts +11 -0
- package/src/wirings/channel/channel-common.ts +2 -2
- package/src/wirings/channel/channel-handler.ts +13 -9
- package/src/wirings/channel/channel-runner.ts +6 -6
- package/src/wirings/channel/channel-store.ts +1 -1
- package/src/wirings/channel/channel.types.ts +4 -8
- package/src/wirings/channel/local/local-channel-runner.test.ts +19 -12
- package/src/wirings/channel/local/local-channel-runner.ts +18 -13
- package/src/wirings/channel/local/local-eventhub-service.test.ts +2 -2
- package/src/wirings/channel/local/local-eventhub-service.ts +2 -2
- package/src/wirings/channel/log-channels.ts +1 -1
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +1 -1
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +37 -32
- package/src/wirings/cli/channel/cli-channel-runner.ts +1 -1
- package/src/wirings/cli/cli-runner.test.ts +1 -1
- package/src/wirings/cli/cli-runner.ts +11 -4
- package/src/wirings/cli/cli.types.ts +3 -3
- package/src/wirings/cli/command-parser.test.ts +1 -1
- package/src/wirings/cli/command-parser.ts +1 -1
- package/src/wirings/http/http-routes.test.ts +1 -1
- package/src/wirings/http/http-runner.test.ts +9 -17
- package/src/wirings/http/http-runner.ts +43 -27
- package/src/wirings/http/http.types.ts +1 -14
- package/src/wirings/http/log-http-routes.ts +1 -1
- package/src/wirings/http/pikku-fetch-http-request.ts +1 -1
- package/src/wirings/http/pikku-fetch-http-response.ts +12 -5
- package/src/wirings/http/routers/http-router.ts +1 -1
- package/src/wirings/http/routers/path-to-regex.test.ts +1 -1
- package/src/wirings/http/routers/path-to-regex.ts +4 -3
- package/src/wirings/mcp/mcp-endpoint-registry.ts +5 -1
- package/src/wirings/mcp/mcp-runner.ts +9 -15
- package/src/wirings/mcp/mcp.types.ts +2 -2
- package/src/wirings/oauth2/oauth2-client.ts +3 -3
- package/src/wirings/oauth2/wire-oauth2-credential.ts +1 -1
- package/src/wirings/queue/index.ts +0 -1
- package/src/wirings/queue/queue-runner.test.ts +52 -25
- package/src/wirings/queue/queue-runner.ts +8 -30
- package/src/wirings/queue/queue.types.ts +3 -13
- package/src/wirings/queue/register-queue-helper.ts +3 -5
- package/src/wirings/queue/validate-worker-config.test.ts +108 -0
- package/src/wirings/queue/validate-worker-config.ts +4 -1
- package/src/wirings/rpc/index.ts +2 -0
- package/src/wirings/rpc/rpc-runner.ts +92 -49
- package/src/wirings/rpc/rpc-types.ts +17 -2
- package/src/wirings/rpc/wire-addon.ts +20 -0
- package/src/wirings/scheduler/index.ts +0 -1
- package/src/wirings/scheduler/log-schedulers.ts +1 -1
- package/src/wirings/scheduler/scheduler-runner.test.ts +49 -60
- package/src/wirings/scheduler/scheduler-runner.ts +9 -56
- package/src/wirings/scheduler/scheduler.types.ts +2 -2
- package/src/wirings/secret/validate-secret-definitions.test.ts +140 -0
- package/src/wirings/trigger/pikku-trigger-service.ts +17 -39
- package/src/wirings/trigger/trigger-runner.test.ts +79 -0
- package/src/wirings/trigger/trigger-runner.ts +8 -11
- package/src/wirings/variable/validate-variable-definitions.test.ts +91 -0
- package/src/wirings/workflow/graph/graph-runner.test.ts +14 -22
- package/src/wirings/workflow/graph/template.test.ts +49 -0
- package/src/wirings/workflow/pikku-workflow-service.test.ts +15 -27
- package/src/wirings/workflow/pikku-workflow-service.ts +29 -45
- package/src/wirings/workflow/workflow-helpers.test.ts +129 -0
- package/src/wirings/workflow/workflow.types.ts +2 -2
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/wirings/workflow/workflow-utils.d.ts +0 -1
- package/dist/wirings/workflow/workflow-utils.js +0 -1
- package/src/wirings/workflow/workflow-utils.ts +0 -0
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { getErrorResponse, PikkuError } from '../../errors/error-handler.js';
|
|
2
2
|
import { PikkuMissingMetaError } from '../../errors/errors.js';
|
|
3
|
-
import {
|
|
4
|
-
import { pikkuState } from '../../pikku-state.js';
|
|
3
|
+
import { getSingletonServices, getCreateWireServices, pikkuState, } from '../../pikku-state.js';
|
|
5
4
|
import { addFunction, runPikkuFunc } from '../../function/function-runner.js';
|
|
6
5
|
import { PikkuSessionService, createMiddlewareSessionWireProps, } from '../../services/user-session-service.js';
|
|
7
|
-
import { rpcService } from '../rpc/rpc-runner.js';
|
|
8
6
|
export const wireScheduler = (scheduledTask) => {
|
|
9
7
|
const meta = pikkuState(null, 'scheduler', 'meta');
|
|
10
8
|
const taskMeta = meta[scheduledTask.name];
|
|
@@ -35,8 +33,9 @@ class ScheduledTaskSkippedError extends PikkuError {
|
|
|
35
33
|
this.name = 'ScheduledTaskSkippedError';
|
|
36
34
|
}
|
|
37
35
|
}
|
|
38
|
-
export async function runScheduledTask({ name, session,
|
|
39
|
-
|
|
36
|
+
export async function runScheduledTask({ name, session, }) {
|
|
37
|
+
const singletonServices = getSingletonServices();
|
|
38
|
+
const createWireServices = getCreateWireServices();
|
|
40
39
|
const task = pikkuState(null, 'scheduler', 'tasks').get(name);
|
|
41
40
|
const meta = pikkuState(null, 'scheduler', 'meta')[name];
|
|
42
41
|
const userSession = new PikkuSessionService();
|
|
@@ -82,27 +81,7 @@ export async function runScheduledTask({ name, session, singletonServices, creat
|
|
|
82
81
|
}
|
|
83
82
|
throw e;
|
|
84
83
|
}
|
|
85
|
-
finally {
|
|
86
|
-
if (wireServices) {
|
|
87
|
-
await closeWireServices(singletonServices.logger, wireServices);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
84
|
}
|
|
91
85
|
export const getScheduledTasks = () => {
|
|
92
86
|
return pikkuState(null, 'scheduler', 'tasks');
|
|
93
87
|
};
|
|
94
|
-
export const createSchedulerRuntimeHandlers = ({ singletonServices, createWireServices, }) => {
|
|
95
|
-
return {
|
|
96
|
-
logger: singletonServices.logger,
|
|
97
|
-
invokeRPC: async (rpcName, data, session) => {
|
|
98
|
-
const wire = session ? { session } : {};
|
|
99
|
-
const rpc = rpcService.getContextRPCService(singletonServices, wire);
|
|
100
|
-
await rpc.invoke(rpcName, data);
|
|
101
|
-
},
|
|
102
|
-
runScheduledTask: async (name) => runScheduledTask({
|
|
103
|
-
name,
|
|
104
|
-
singletonServices,
|
|
105
|
-
createWireServices,
|
|
106
|
-
}),
|
|
107
|
-
};
|
|
108
|
-
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { CoreUserSession, CorePikkuMiddleware, CommonWireMeta } from '../../types/core.types.js';
|
|
2
|
-
import { CorePikkuFunctionConfig, CorePikkuFunctionSessionless } from '../../function/functions.types.js';
|
|
1
|
+
import type { CoreUserSession, CorePikkuMiddleware, CommonWireMeta } from '../../types/core.types.js';
|
|
2
|
+
import type { CorePikkuFunctionConfig, CorePikkuFunctionSessionless } from '../../function/functions.types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Represents metadata for scheduled tasks, including title, schedule, and documentation.
|
|
5
5
|
*/
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { TriggerService } from '../../services/trigger-service.js';
|
|
1
|
+
import type { TriggerInstance } from './trigger.types.js';
|
|
2
|
+
import type { TriggerService } from '../../services/trigger-service.js';
|
|
4
3
|
export type TriggerTarget = {
|
|
5
4
|
targetType: 'rpc' | 'workflow';
|
|
6
5
|
targetName: string;
|
|
@@ -12,9 +11,6 @@ export type TriggerSourceInfo = {
|
|
|
12
11
|
};
|
|
13
12
|
export declare abstract class PikkuTriggerService implements TriggerService {
|
|
14
13
|
protected activeTriggers: Map<string, TriggerInstance>;
|
|
15
|
-
protected singletonServices?: CoreSingletonServices;
|
|
16
|
-
protected createWireServices?: CreateWireServices<CoreSingletonServices, CoreServices, CoreUserSession>;
|
|
17
|
-
setServices(singletonServices: CoreSingletonServices, createWireServices?: CreateWireServices<CoreSingletonServices, CoreServices, CoreUserSession>): void;
|
|
18
14
|
abstract start(): Promise<void>;
|
|
19
15
|
stop(): Promise<void>;
|
|
20
16
|
protected getTriggerTargets(): Map<string, TriggerTarget[]>;
|
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
import { rpcService } from '../rpc/rpc-runner.js';
|
|
2
2
|
import { setupTrigger } from './trigger-runner.js';
|
|
3
|
-
import { pikkuState } from '../../pikku-state.js';
|
|
3
|
+
import { getSingletonServices, getCreateWireServices, pikkuState, } from '../../pikku-state.js';
|
|
4
4
|
export class PikkuTriggerService {
|
|
5
5
|
activeTriggers = new Map();
|
|
6
|
-
singletonServices;
|
|
7
|
-
createWireServices;
|
|
8
|
-
setServices(singletonServices, createWireServices) {
|
|
9
|
-
this.singletonServices = singletonServices;
|
|
10
|
-
this.createWireServices = createWireServices;
|
|
11
|
-
}
|
|
12
6
|
async stop() {
|
|
7
|
+
const singletonServices = getSingletonServices();
|
|
13
8
|
for (const [name, instance] of this.activeTriggers) {
|
|
14
9
|
try {
|
|
15
10
|
await instance.teardown();
|
|
16
|
-
|
|
11
|
+
singletonServices.logger.info(`Stopped trigger: ${name}`);
|
|
17
12
|
}
|
|
18
13
|
catch (error) {
|
|
19
|
-
|
|
14
|
+
singletonServices.logger.error(`Error stopping trigger ${name}: ${error}`);
|
|
20
15
|
}
|
|
21
16
|
}
|
|
22
17
|
this.activeTriggers.clear();
|
|
@@ -44,15 +39,15 @@ export class PikkuTriggerService {
|
|
|
44
39
|
async setupTriggerInstance(name, input, onTrigger) {
|
|
45
40
|
return setupTrigger({
|
|
46
41
|
name,
|
|
47
|
-
singletonServices: this.singletonServices,
|
|
48
|
-
createWireServices: this.createWireServices,
|
|
49
42
|
input,
|
|
50
43
|
onTrigger,
|
|
51
44
|
});
|
|
52
45
|
}
|
|
53
46
|
async onTriggerFire(triggerName, targets, data) {
|
|
54
|
-
const
|
|
55
|
-
const
|
|
47
|
+
const singletonServices = getSingletonServices();
|
|
48
|
+
const createWireServices = getCreateWireServices();
|
|
49
|
+
const wireServices = await createWireServices?.(singletonServices, {});
|
|
50
|
+
const services = { ...singletonServices, ...wireServices };
|
|
56
51
|
const rpc = rpcService.getContextRPCService(services, {}, false);
|
|
57
52
|
for (const target of targets) {
|
|
58
53
|
try {
|
|
@@ -60,15 +55,15 @@ export class PikkuTriggerService {
|
|
|
60
55
|
await rpc.startWorkflow(target.targetName, data, {
|
|
61
56
|
startNode: target.startNode,
|
|
62
57
|
});
|
|
63
|
-
|
|
58
|
+
singletonServices.logger.info(`Trigger '${triggerName}' started workflow '${target.targetName}'`);
|
|
64
59
|
}
|
|
65
60
|
else {
|
|
66
61
|
await rpc.invoke(target.targetName, data);
|
|
67
|
-
|
|
62
|
+
singletonServices.logger.info(`Trigger '${triggerName}' invoked RPC '${target.targetName}'`);
|
|
68
63
|
}
|
|
69
64
|
}
|
|
70
65
|
catch (error) {
|
|
71
|
-
|
|
66
|
+
singletonServices.logger.error(`Error invoking ${target.targetType} '${target.targetName}' from trigger '${triggerName}': ${error}`);
|
|
72
67
|
}
|
|
73
68
|
}
|
|
74
69
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { CreateWireServices, type CoreSingletonServices } from '../../types/core.types.js';
|
|
2
1
|
import type { CoreTrigger, CoreTriggerSource, TriggerInstance } from './trigger.types.js';
|
|
3
2
|
/**
|
|
4
3
|
* Registers a trigger with the Pikku framework.
|
|
@@ -17,8 +16,6 @@ export declare const wireTriggerSource: <TInput = unknown, TOutput = unknown>(so
|
|
|
17
16
|
*/
|
|
18
17
|
export type SetupTriggerParams<TInput = unknown, TOutput = unknown> = {
|
|
19
18
|
name: string;
|
|
20
|
-
singletonServices: CoreSingletonServices;
|
|
21
|
-
createWireServices?: CreateWireServices;
|
|
22
19
|
input?: TInput;
|
|
23
20
|
onTrigger: (data: TOutput) => void | Promise<void>;
|
|
24
21
|
};
|
|
@@ -27,11 +24,10 @@ export type SetupTriggerParams<TInput = unknown, TOutput = unknown> = {
|
|
|
27
24
|
* Returns a TriggerInstance with a teardown function.
|
|
28
25
|
*
|
|
29
26
|
* @param name - The trigger name (must be registered via wireTrigger)
|
|
30
|
-
* @param singletonServices - The singleton services
|
|
31
27
|
* @param input - The input to pass to the trigger function
|
|
32
28
|
* @param onTrigger - Callback invoked when the trigger fires
|
|
33
29
|
*/
|
|
34
|
-
export declare function setupTrigger<TInput = unknown, TOutput = unknown>({ name,
|
|
30
|
+
export declare function setupTrigger<TInput = unknown, TOutput = unknown>({ name, input, onTrigger, }: SetupTriggerParams<TInput, TOutput>): Promise<TriggerInstance>;
|
|
35
31
|
/**
|
|
36
32
|
* Gets all registered triggers
|
|
37
33
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { pikkuState } from '../../pikku-state.js';
|
|
1
|
+
import { pikkuState, getSingletonServices, getCreateWireServices, } from '../../pikku-state.js';
|
|
2
2
|
import { addFunction, runPikkuFunc } from '../../function/function-runner.js';
|
|
3
3
|
import { PikkuMissingMetaError } from '../../errors/errors.js';
|
|
4
4
|
/**
|
|
@@ -39,11 +39,12 @@ export const wireTriggerSource = (source) => {
|
|
|
39
39
|
* Returns a TriggerInstance with a teardown function.
|
|
40
40
|
*
|
|
41
41
|
* @param name - The trigger name (must be registered via wireTrigger)
|
|
42
|
-
* @param singletonServices - The singleton services
|
|
43
42
|
* @param input - The input to pass to the trigger function
|
|
44
43
|
* @param onTrigger - Callback invoked when the trigger fires
|
|
45
44
|
*/
|
|
46
|
-
export async function setupTrigger({ name,
|
|
45
|
+
export async function setupTrigger({ name, input, onTrigger, }) {
|
|
46
|
+
const singletonServices = getSingletonServices();
|
|
47
|
+
const createWireServices = getCreateWireServices();
|
|
47
48
|
const source = pikkuState(null, 'trigger', 'triggerSources').get(name);
|
|
48
49
|
const sourceMeta = pikkuState(null, 'trigger', 'sourceMeta')[name];
|
|
49
50
|
if (!source) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { SerializedError } from '../../types/core.types.js';
|
|
2
2
|
import type { StepState, WorkflowRun, WorkflowRunWire, WorkflowStatus, WorkflowStepOptions } from './workflow.types.js';
|
|
3
|
-
import { WorkflowService } from '../../services/workflow-service.js';
|
|
3
|
+
import type { WorkflowService } from '../../services/workflow-service.js';
|
|
4
4
|
import { PikkuError } from '../../errors/error-handler.js';
|
|
5
5
|
/**
|
|
6
6
|
* Exception thrown when workflow needs to pause for async step
|
|
@@ -46,9 +46,6 @@ export declare class WorkflowStepNameNotString extends Error {
|
|
|
46
46
|
* Combines orchestration and step execution
|
|
47
47
|
*/
|
|
48
48
|
export declare abstract class PikkuWorkflowService implements WorkflowService {
|
|
49
|
-
private config;
|
|
50
|
-
private singletonServices;
|
|
51
|
-
private createWireServices;
|
|
52
49
|
private inlineRuns;
|
|
53
50
|
constructor();
|
|
54
51
|
/**
|
|
@@ -63,7 +60,6 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
63
60
|
* Unregister a run from inline tracking
|
|
64
61
|
*/
|
|
65
62
|
unregisterInlineRun(runId: string): void;
|
|
66
|
-
setServices(singletonServices: CoreSingletonServices, createWireServices: CreateWireServices | undefined, { workflow }: CoreConfig): void;
|
|
67
63
|
registerWorkflowVersions(): Promise<void>;
|
|
68
64
|
abstract createRun(workflowName: string, input: any, inline: boolean, graphHash: string, wire: WorkflowRunWire): Promise<string>;
|
|
69
65
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { runPikkuFunc } from '../../function/function-runner.js';
|
|
2
|
-
import { pikkuState } from '../../pikku-state.js';
|
|
2
|
+
import { getSingletonServices, getCreateWireServices, pikkuState, } from '../../pikku-state.js';
|
|
3
3
|
import { getDurationInMilliseconds } from '../../time-utils.js';
|
|
4
4
|
import { continueGraph, executeGraphStep, runWorkflowGraph, runFromMeta, } from './graph/graph-runner.js';
|
|
5
5
|
import { PikkuError, addError } from '../../errors/error-handler.js';
|
|
@@ -83,9 +83,6 @@ const WORKFLOW_END_STATES = new Set([
|
|
|
83
83
|
* Combines orchestration and step execution
|
|
84
84
|
*/
|
|
85
85
|
export class PikkuWorkflowService {
|
|
86
|
-
config;
|
|
87
|
-
singletonServices;
|
|
88
|
-
createWireServices;
|
|
89
86
|
inlineRuns = new Set();
|
|
90
87
|
constructor() { }
|
|
91
88
|
/**
|
|
@@ -106,17 +103,6 @@ export class PikkuWorkflowService {
|
|
|
106
103
|
unregisterInlineRun(runId) {
|
|
107
104
|
this.inlineRuns.delete(runId);
|
|
108
105
|
}
|
|
109
|
-
setServices(singletonServices, createWireServices, { workflow }) {
|
|
110
|
-
this.singletonServices = singletonServices;
|
|
111
|
-
this.createWireServices = createWireServices;
|
|
112
|
-
this.config = {
|
|
113
|
-
retries: workflow?.retries ?? 0,
|
|
114
|
-
retryDelay: workflow?.retryDelay ?? 0,
|
|
115
|
-
orchestratorQueueName: workflow?.orchestratorQueueName ?? 'pikku-workflow-orchestrator',
|
|
116
|
-
stepWorkerQueueName: workflow?.stepWorkerQueueName ?? 'pikku-workflow-step-worker',
|
|
117
|
-
sleeperRPCName: workflow?.sleeperRPCName ?? 'pikkuWorkflowSleeper',
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
106
|
async registerWorkflowVersions() {
|
|
121
107
|
const allMeta = pikkuState(null, 'workflows', 'meta');
|
|
122
108
|
for (const [name, meta] of Object.entries(allMeta)) {
|
|
@@ -178,7 +164,7 @@ export class PikkuWorkflowService {
|
|
|
178
164
|
}
|
|
179
165
|
// Check if this is a graph workflow (source === 'graph')
|
|
180
166
|
if (workflowMeta.source === 'graph') {
|
|
181
|
-
const shouldInline = options?.inline || !
|
|
167
|
+
const shouldInline = options?.inline || !getSingletonServices()?.queueService;
|
|
182
168
|
return runWorkflowGraph(this, name, input, rpcService, shouldInline, options?.startNode, wire);
|
|
183
169
|
}
|
|
184
170
|
// DSL workflow - check registration exists
|
|
@@ -194,7 +180,7 @@ export class PikkuWorkflowService {
|
|
|
194
180
|
if (options?.inline) {
|
|
195
181
|
this.inlineRuns.add(runId);
|
|
196
182
|
}
|
|
197
|
-
if (options?.inline || !
|
|
183
|
+
if (options?.inline || !getSingletonServices()?.queueService) {
|
|
198
184
|
this.runWorkflowJob(runId, rpcService)
|
|
199
185
|
.catch(() => { })
|
|
200
186
|
.finally(() => {
|
|
@@ -255,9 +241,9 @@ export class PikkuWorkflowService {
|
|
|
255
241
|
const wire = { workflow: workflowWire };
|
|
256
242
|
try {
|
|
257
243
|
const result = await runPikkuFunc('workflow', workflowMeta.name, workflowMeta.pikkuFuncId, {
|
|
258
|
-
singletonServices:
|
|
244
|
+
singletonServices: getSingletonServices(),
|
|
259
245
|
wire,
|
|
260
|
-
createWireServices:
|
|
246
|
+
createWireServices: getCreateWireServices(),
|
|
261
247
|
data: () => run.input,
|
|
262
248
|
});
|
|
263
249
|
await this.updateRunStatus(runId, 'completed', result);
|
|
@@ -409,10 +395,10 @@ export class PikkuWorkflowService {
|
|
|
409
395
|
}
|
|
410
396
|
}
|
|
411
397
|
verifyQueueService() {
|
|
412
|
-
if (!
|
|
398
|
+
if (!getSingletonServices()?.queueService) {
|
|
413
399
|
throw new Error('QueueService not configured. Remote workflows require a queue service.');
|
|
414
400
|
}
|
|
415
|
-
return
|
|
401
|
+
return getSingletonServices().queueService;
|
|
416
402
|
}
|
|
417
403
|
async rpcStep(runId, stepName, rpcName, data, rpcService, stepOptions) {
|
|
418
404
|
// Check if step already exists
|
|
@@ -445,11 +431,11 @@ export class PikkuWorkflowService {
|
|
|
445
431
|
// Step is pending - schedule it
|
|
446
432
|
await this.setStepScheduled(stepState.stepId);
|
|
447
433
|
// Enqueue step worker (unless inline mode)
|
|
448
|
-
if (!this.isInline(runId) &&
|
|
434
|
+
if (!this.isInline(runId) && getSingletonServices().queueService) {
|
|
449
435
|
// Map step retry options to queue job options
|
|
450
436
|
const retries = stepOptions?.retries ?? 0;
|
|
451
437
|
const retryDelay = stepOptions?.retryDelay;
|
|
452
|
-
await
|
|
438
|
+
await getSingletonServices().queueService.add(this.getConfig().stepWorkerQueueName, {
|
|
453
439
|
runId,
|
|
454
440
|
stepName,
|
|
455
441
|
rpcName,
|
|
@@ -609,9 +595,9 @@ export class PikkuWorkflowService {
|
|
|
609
595
|
// Step is pending - schedule it
|
|
610
596
|
await this.setStepScheduled(stepState.stepId);
|
|
611
597
|
// Check if inline mode or no scheduler service
|
|
612
|
-
if (!this.isInline(runId) &&
|
|
598
|
+
if (!this.isInline(runId) && getSingletonServices().schedulerService) {
|
|
613
599
|
// Remote mode - schedule sleep via scheduler service
|
|
614
|
-
await
|
|
600
|
+
await getSingletonServices().schedulerService.scheduleRPC(duration, this.getConfig().sleeperRPCName, {
|
|
615
601
|
runId,
|
|
616
602
|
stepId: stepState.stepId,
|
|
617
603
|
});
|
|
@@ -693,9 +679,14 @@ export class PikkuWorkflowService {
|
|
|
693
679
|
}
|
|
694
680
|
}
|
|
695
681
|
getConfig() {
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
682
|
+
const singletonServices = getSingletonServices();
|
|
683
|
+
const workflow = singletonServices.config?.workflow;
|
|
684
|
+
return {
|
|
685
|
+
retries: workflow?.retries ?? 0,
|
|
686
|
+
retryDelay: workflow?.retryDelay ?? 0,
|
|
687
|
+
orchestratorQueueName: workflow?.orchestratorQueueName ?? 'pikku-workflow-orchestrator',
|
|
688
|
+
stepWorkerQueueName: workflow?.stepWorkerQueueName ?? 'pikku-workflow-step-worker',
|
|
689
|
+
sleeperRPCName: workflow?.sleeperRPCName ?? 'pikkuWorkflowSleeper',
|
|
690
|
+
};
|
|
700
691
|
}
|
|
701
692
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { SerializedError, CommonWireMeta } from '../../types/core.types.js';
|
|
2
|
-
import { CorePikkuFunctionConfig } from '../../function/functions.types.js';
|
|
1
|
+
import type { SerializedError, CommonWireMeta } from '../../types/core.types.js';
|
|
2
|
+
import type { CorePikkuFunctionConfig } from '../../function/functions.types.js';
|
|
3
3
|
export type { WorkflowService } from '../../services/workflow-service.js';
|
|
4
4
|
export type { WorkflowStepOptions, WorkflowWireDoRPC, WorkflowWireDoInline, WorkflowWireSleep, WorkflowWireSuspend, InputSource, OutputBinding, RpcStepMeta, SimpleCondition, Condition, BranchCase, BranchStepMeta, ParallelGroupStepMeta, FanoutStepMeta, ReturnStepMeta, InlineStepMeta, SleepStepMeta, CancelStepMeta, SetStepMeta, SwitchCaseMeta, SwitchStepMeta, FilterStepMeta, ArrayPredicateStepMeta, WorkflowStepMeta, WorkflowStepWire, PikkuWorkflowWire, } from './dsl/workflow-dsl.types.js';
|
|
5
5
|
import type { WorkflowStepMeta } from './dsl/workflow-dsl.types.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/core",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"author": "yasser.fadl@gmail.com",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"./services": "./dist/services/index.js",
|
|
41
41
|
"./services/local-content": "./dist/services/local-content.js",
|
|
42
42
|
"./services/gopass-secrets": "./dist/services/gopass-secrets.js",
|
|
43
|
+
"./internal": "./dist/internal.js",
|
|
43
44
|
"./schema": "./dist/schema.js"
|
|
44
45
|
},
|
|
45
46
|
"dependencies": {
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { describe, test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert'
|
|
3
|
+
import { encryptJSON, decryptJSON } from './crypto-utils.js'
|
|
4
|
+
|
|
5
|
+
describe('encryptJSON / decryptJSON', () => {
|
|
6
|
+
const secret = 'test-secret-key-for-encryption'
|
|
7
|
+
|
|
8
|
+
test('should round-trip a simple object', async () => {
|
|
9
|
+
const original = { hello: 'world', num: 42 }
|
|
10
|
+
const encrypted = await encryptJSON(secret, original)
|
|
11
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
12
|
+
assert.deepStrictEqual(decrypted, original)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('should round-trip a string value', async () => {
|
|
16
|
+
const original = 'just a string'
|
|
17
|
+
const encrypted = await encryptJSON(secret, original)
|
|
18
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
19
|
+
assert.strictEqual(decrypted, original)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('should round-trip a number', async () => {
|
|
23
|
+
const original = 12345
|
|
24
|
+
const encrypted = await encryptJSON(secret, original)
|
|
25
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
26
|
+
assert.strictEqual(decrypted, original)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('should round-trip null', async () => {
|
|
30
|
+
const encrypted = await encryptJSON(secret, null)
|
|
31
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
32
|
+
assert.strictEqual(decrypted, null)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('should round-trip a boolean', async () => {
|
|
36
|
+
const encrypted = await encryptJSON(secret, true)
|
|
37
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
38
|
+
assert.strictEqual(decrypted, true)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('should round-trip a nested object', async () => {
|
|
42
|
+
const original = { user: { id: 1, roles: ['admin', 'user'] }, active: true }
|
|
43
|
+
const encrypted = await encryptJSON(secret, original)
|
|
44
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
45
|
+
assert.deepStrictEqual(decrypted, original)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('should round-trip an array', async () => {
|
|
49
|
+
const original = [1, 'two', { three: 3 }]
|
|
50
|
+
const encrypted = await encryptJSON(secret, original)
|
|
51
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
52
|
+
assert.deepStrictEqual(decrypted, original)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('should produce different ciphertexts for same input (random IV)', async () => {
|
|
56
|
+
const value = { same: 'data' }
|
|
57
|
+
const enc1 = await encryptJSON(secret, value)
|
|
58
|
+
const enc2 = await encryptJSON(secret, value)
|
|
59
|
+
assert.notStrictEqual(enc1, enc2)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('should produce a base64url string (no +, /, or = characters)', async () => {
|
|
63
|
+
const encrypted = await encryptJSON(secret, { test: 'base64url' })
|
|
64
|
+
assert.ok(!/[+/=]/.test(encrypted), `Expected base64url, got: ${encrypted}`)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test('should fail to decrypt with wrong secret', async () => {
|
|
68
|
+
const encrypted = await encryptJSON(secret, { data: 'sensitive' })
|
|
69
|
+
await assert.rejects(
|
|
70
|
+
() => decryptJSON('wrong-secret', encrypted),
|
|
71
|
+
(err: any) => err instanceof Error
|
|
72
|
+
)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test('should reject an invalid encrypted payload (too short)', async () => {
|
|
76
|
+
await assert.rejects(() => decryptJSON(secret, 'dG9vc2hvcnQ'), {
|
|
77
|
+
message: 'Invalid encrypted payload',
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test('should reject empty string', async () => {
|
|
82
|
+
await assert.rejects(() => decryptJSON(secret, ''), {
|
|
83
|
+
message: 'Invalid encrypted payload',
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
test('should reject corrupted ciphertext', async () => {
|
|
88
|
+
const encrypted = await encryptJSON(secret, { data: 'test' })
|
|
89
|
+
const corrupted = encrypted.slice(0, -4) + 'XXXX'
|
|
90
|
+
await assert.rejects(
|
|
91
|
+
() => decryptJSON(secret, corrupted),
|
|
92
|
+
(err: any) => err instanceof Error
|
|
93
|
+
)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test('should handle unicode content', async () => {
|
|
97
|
+
const original = { emoji: '🎉', japanese: 'こんにちは', arabic: 'مرحبا' }
|
|
98
|
+
const encrypted = await encryptJSON(secret, original)
|
|
99
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
100
|
+
assert.deepStrictEqual(decrypted, original)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
test('should handle empty object', async () => {
|
|
104
|
+
const original = {}
|
|
105
|
+
const encrypted = await encryptJSON(secret, original)
|
|
106
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
107
|
+
assert.deepStrictEqual(decrypted, original)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('should handle large payload', async () => {
|
|
111
|
+
const original = { data: 'x'.repeat(10000) }
|
|
112
|
+
const encrypted = await encryptJSON(secret, original)
|
|
113
|
+
const decrypted = await decryptJSON(secret, encrypted)
|
|
114
|
+
assert.deepStrictEqual(decrypted, original)
|
|
115
|
+
})
|
|
116
|
+
})
|
package/src/errors/error.test.ts
CHANGED
|
@@ -1,7 +1,75 @@
|
|
|
1
1
|
import { test, describe } from 'node:test'
|
|
2
2
|
import assert from 'assert'
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
getErrorResponse,
|
|
5
|
+
addError,
|
|
6
|
+
addErrors,
|
|
7
|
+
PikkuError,
|
|
8
|
+
} from './error-handler.js'
|
|
9
|
+
import {
|
|
10
|
+
BadRequestError,
|
|
11
|
+
UnauthorizedError,
|
|
12
|
+
MissingSessionError,
|
|
13
|
+
InvalidSessionError,
|
|
14
|
+
PaymentRequiredError,
|
|
15
|
+
ForbiddenError,
|
|
16
|
+
InvalidOriginError,
|
|
17
|
+
NotFoundError,
|
|
18
|
+
MethodNotAllowedError,
|
|
19
|
+
NotAcceptableError,
|
|
20
|
+
ProxyAuthenticationRequiredError,
|
|
21
|
+
RequestTimeoutError,
|
|
22
|
+
ConflictError,
|
|
23
|
+
GoneError,
|
|
24
|
+
LengthRequiredError,
|
|
25
|
+
PreconditionFailedError,
|
|
26
|
+
PayloadTooLargeError,
|
|
27
|
+
URITooLongError,
|
|
28
|
+
UnsupportedMediaTypeError,
|
|
29
|
+
RangeNotSatisfiableError,
|
|
30
|
+
ExpectationFailedError,
|
|
31
|
+
UnprocessableContentError,
|
|
32
|
+
LockedError,
|
|
33
|
+
TooManyRequestsError,
|
|
34
|
+
InternalServerError,
|
|
35
|
+
NotImplementedError,
|
|
36
|
+
BadGatewayError,
|
|
37
|
+
ServiceUnavailableError,
|
|
38
|
+
GatewayTimeoutError,
|
|
39
|
+
HTTPVersionNotSupportedError,
|
|
40
|
+
MaxComputeTimeReachedError,
|
|
41
|
+
MissingSchemaError,
|
|
42
|
+
InvalidMiddlewareWireError,
|
|
43
|
+
PikkuMissingMetaError,
|
|
44
|
+
} from './errors.js'
|
|
45
|
+
|
|
46
|
+
describe('PikkuError', () => {
|
|
47
|
+
test('should create error with default message', () => {
|
|
48
|
+
const error = new PikkuError()
|
|
49
|
+
assert.strictEqual(error.message, 'An error occurred')
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test('should create error with custom message', () => {
|
|
53
|
+
const error = new PikkuError('custom message')
|
|
54
|
+
assert.strictEqual(error.message, 'custom message')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('should be instance of Error', () => {
|
|
58
|
+
const error = new PikkuError()
|
|
59
|
+
assert.ok(error instanceof Error)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('should be instance of PikkuError', () => {
|
|
63
|
+
const error = new PikkuError()
|
|
64
|
+
assert.ok(error instanceof PikkuError)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test('subclasses should be instanceof PikkuError', () => {
|
|
68
|
+
assert.ok(new BadRequestError() instanceof PikkuError)
|
|
69
|
+
assert.ok(new NotFoundError() instanceof PikkuError)
|
|
70
|
+
assert.ok(new ForbiddenError() instanceof PikkuError)
|
|
71
|
+
})
|
|
72
|
+
})
|
|
5
73
|
|
|
6
74
|
describe('getErrorResponse', () => {
|
|
7
75
|
test('should return the correct error response for BadRequestError', () => {
|
|
@@ -52,3 +120,76 @@ describe('getErrorResponse', () => {
|
|
|
52
120
|
assert.deepStrictEqual(response, customErrorDetails)
|
|
53
121
|
})
|
|
54
122
|
})
|
|
123
|
+
|
|
124
|
+
describe('addErrors', () => {
|
|
125
|
+
test('should register multiple errors at once', () => {
|
|
126
|
+
class ErrA extends Error {}
|
|
127
|
+
class ErrB extends Error {}
|
|
128
|
+
addErrors([
|
|
129
|
+
[ErrA, { status: 450, message: 'Error A' }],
|
|
130
|
+
[ErrB, { status: 451, message: 'Error B' }],
|
|
131
|
+
])
|
|
132
|
+
assert.deepStrictEqual(getErrorResponse(new ErrA()), {
|
|
133
|
+
status: 450,
|
|
134
|
+
message: 'Error A',
|
|
135
|
+
})
|
|
136
|
+
assert.deepStrictEqual(getErrorResponse(new ErrB()), {
|
|
137
|
+
status: 451,
|
|
138
|
+
message: 'Error B',
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
describe('all built-in error classes', () => {
|
|
144
|
+
const errorCases: [string, any, number][] = [
|
|
145
|
+
['InvalidMiddlewareWireError', InvalidMiddlewareWireError, 500],
|
|
146
|
+
['PikkuMissingMetaError', PikkuMissingMetaError, 500],
|
|
147
|
+
['BadRequestError', BadRequestError, 400],
|
|
148
|
+
['UnauthorizedError', UnauthorizedError, 401],
|
|
149
|
+
['MissingSessionError', MissingSessionError, 401],
|
|
150
|
+
['InvalidSessionError', InvalidSessionError, 401],
|
|
151
|
+
['PaymentRequiredError', PaymentRequiredError, 402],
|
|
152
|
+
['ForbiddenError', ForbiddenError, 403],
|
|
153
|
+
['InvalidOriginError', InvalidOriginError, 403],
|
|
154
|
+
['NotFoundError', NotFoundError, 404],
|
|
155
|
+
['MethodNotAllowedError', MethodNotAllowedError, 405],
|
|
156
|
+
['NotAcceptableError', NotAcceptableError, 406],
|
|
157
|
+
['ProxyAuthenticationRequiredError', ProxyAuthenticationRequiredError, 407],
|
|
158
|
+
['RequestTimeoutError', RequestTimeoutError, 408],
|
|
159
|
+
['ConflictError', ConflictError, 409],
|
|
160
|
+
['GoneError', GoneError, 410],
|
|
161
|
+
['LengthRequiredError', LengthRequiredError, 411],
|
|
162
|
+
['PreconditionFailedError', PreconditionFailedError, 412],
|
|
163
|
+
['PayloadTooLargeError', PayloadTooLargeError, 413],
|
|
164
|
+
['URITooLongError', URITooLongError, 414],
|
|
165
|
+
['UnsupportedMediaTypeError', UnsupportedMediaTypeError, 415],
|
|
166
|
+
['RangeNotSatisfiableError', RangeNotSatisfiableError, 416],
|
|
167
|
+
['ExpectationFailedError', ExpectationFailedError, 417],
|
|
168
|
+
['UnprocessableContentError', UnprocessableContentError, 422],
|
|
169
|
+
['LockedError', LockedError, 423],
|
|
170
|
+
['TooManyRequestsError', TooManyRequestsError, 429],
|
|
171
|
+
['InternalServerError', InternalServerError, 500],
|
|
172
|
+
['NotImplementedError', NotImplementedError, 501],
|
|
173
|
+
['BadGatewayError', BadGatewayError, 502],
|
|
174
|
+
['ServiceUnavailableError', ServiceUnavailableError, 503],
|
|
175
|
+
['GatewayTimeoutError', GatewayTimeoutError, 504],
|
|
176
|
+
['HTTPVersionNotSupportedError', HTTPVersionNotSupportedError, 505],
|
|
177
|
+
['MaxComputeTimeReachedError', MaxComputeTimeReachedError, 524],
|
|
178
|
+
['MissingSchemaError', MissingSchemaError, 500],
|
|
179
|
+
]
|
|
180
|
+
|
|
181
|
+
for (const [name, ErrorClass, expectedStatus] of errorCases) {
|
|
182
|
+
test(`${name} should map to status ${expectedStatus}`, () => {
|
|
183
|
+
const error = new ErrorClass()
|
|
184
|
+
const response = getErrorResponse(error)
|
|
185
|
+
assert.ok(response, `Expected error response for ${name}`)
|
|
186
|
+
assert.strictEqual(response!.status, expectedStatus)
|
|
187
|
+
assert.ok(response!.message.length > 0)
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
test(`${name} should be instance of PikkuError`, () => {
|
|
191
|
+
const error = new ErrorClass()
|
|
192
|
+
assert.ok(error instanceof PikkuError)
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
})
|
package/src/errors/errors.ts
CHANGED
|
@@ -76,6 +76,16 @@ addError(ForbiddenError, {
|
|
|
76
76
|
'The client does not have permission to access the requested resource.',
|
|
77
77
|
})
|
|
78
78
|
|
|
79
|
+
/**
|
|
80
|
+
* The session is readonly and cannot access a non-readonly function.
|
|
81
|
+
* @group Error
|
|
82
|
+
*/
|
|
83
|
+
export class ReadonlySessionError extends PikkuError {}
|
|
84
|
+
addError(ReadonlySessionError, {
|
|
85
|
+
status: 403,
|
|
86
|
+
message: 'The session is readonly and cannot access this function.',
|
|
87
|
+
})
|
|
88
|
+
|
|
79
89
|
/**
|
|
80
90
|
* The request was made from an origin that is not permitted to access this resource.
|
|
81
91
|
* @group Error
|
package/src/errors/index.ts
CHANGED