@pikku/core 0.12.39 → 0.12.41
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 +29 -0
- package/dist/function/function-runner.d.ts +2 -2
- package/dist/function/functions.types.d.ts +6 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/services/credential-wire-service.d.ts +2 -2
- package/dist/services/in-memory-workflow-service.d.ts +1 -0
- package/dist/services/in-memory-workflow-service.js +3 -0
- package/dist/services/pikku-user-id.d.ts +2 -2
- package/dist/services/workflow-service.d.ts +1 -0
- package/dist/types/core.types.d.ts +24 -7
- package/dist/utils.d.ts +3 -1
- package/dist/utils.js +2 -0
- package/dist/wirings/channel/channel-middleware-runner.d.ts +2 -2
- package/dist/wirings/channel/local/local-eventhub-service.js +7 -4
- package/dist/wirings/rpc/rpc-runner.d.ts +4 -4
- package/dist/wirings/rpc/rpc-runner.js +0 -16
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +5 -2
- package/dist/wirings/workflow/pikku-workflow-service.js +17 -17
- package/package.json +1 -1
- package/src/function/function-runner.ts +3 -2
- package/src/function/functions.types.ts +6 -2
- package/src/index.ts +7 -1
- package/src/services/credential-wire-service.ts +2 -2
- package/src/services/in-memory-workflow-service.test.ts +2 -2
- package/src/services/in-memory-workflow-service.ts +7 -1
- package/src/services/pikku-user-id.ts +2 -2
- package/src/services/workflow-service.ts +1 -0
- package/src/types/core.types.ts +28 -8
- package/src/utils.ts +12 -1
- package/src/wirings/ai-agent/ai-agent-prepare.ts +2 -2
- package/src/wirings/channel/channel-common.ts +8 -2
- package/src/wirings/channel/channel-middleware-runner.ts +3 -3
- package/src/wirings/channel/local/local-channel-runner.ts +7 -3
- package/src/wirings/channel/local/local-eventhub-service.ts +6 -3
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +4 -4
- package/src/wirings/cli/cli-runner.ts +2 -2
- package/src/wirings/gateway/gateway-runner.ts +8 -2
- package/src/wirings/http/http-runner.ts +3 -2
- package/src/wirings/mcp/mcp-runner.ts +2 -2
- package/src/wirings/queue/queue-runner.ts +2 -2
- package/src/wirings/rpc/rpc-runner.ts +12 -24
- package/src/wirings/scheduler/scheduler-runner.ts +2 -2
- package/src/wirings/trigger/trigger-runner.ts +2 -2
- package/src/wirings/workflow/pikku-workflow-service.test.ts +16 -26
- package/src/wirings/workflow/pikku-workflow-service.ts +36 -24
- package/src/wirings/workflow/workflow-dispatch-durability.test.ts +3 -3
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
## 0.12.41
|
|
2
|
+
|
|
3
|
+
### Patch Changes
|
|
4
|
+
|
|
5
|
+
- 04db6a2: Make `rpc` a required property on `PikkuWire`. It is always lazily initialised by the function runner on every invocation regardless of wire type, so marking it optional was misleading.
|
|
6
|
+
|
|
7
|
+
## 0.12.40
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- ba1ab08: refactor(workflow): replace `inline: false` with `workflowQueued: true` on function meta
|
|
12
|
+
|
|
13
|
+
The per-function workflow dispatch flag has been renamed from the confusing
|
|
14
|
+
negative `inline: false` to the explicit positive `workflowQueued: true`.
|
|
15
|
+
Two companion fields are also added: `workflowRetries` and `workflowTimeout`
|
|
16
|
+
as function-level equivalents of the per-call-site `NodeOptions` fields.
|
|
17
|
+
|
|
18
|
+
**Breaking change (patch — flag was undocumented):** rename `inline: false`
|
|
19
|
+
to `workflowQueued: true` on any `pikkuSessionlessFunc` / `pikkuFunc` that
|
|
20
|
+
dispatches its workflow steps via the queue.
|
|
21
|
+
|
|
22
|
+
**Behaviour change:** a step marked `workflowQueued: true` now throws if no
|
|
23
|
+
queue service is configured, instead of silently falling back to inline
|
|
24
|
+
execution.
|
|
25
|
+
|
|
26
|
+
**Bug fix:** `post-process.ts` was registering `wf-step-*` queues for every
|
|
27
|
+
workflow step node; it now only registers them for steps that are actually
|
|
28
|
+
`workflowQueued: true`, avoiding spurious queue resource allocation.
|
|
29
|
+
|
|
1
30
|
## 0.12.39
|
|
2
31
|
|
|
3
32
|
### Patch Changes
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CoreUserSession, CorePikkuMiddleware, PikkuWiringTypes,
|
|
1
|
+
import type { CoreUserSession, CorePikkuMiddleware, PikkuWiringTypes, PikkuRawWire, MiddlewareMetadata, PermissionMetadata, CoreSingletonServices, CreateWireServices } from '../types/core.types.js';
|
|
2
2
|
import type { CorePikkuChannelMiddleware } from '../wirings/channel/channel.types.js';
|
|
3
3
|
import type { CorePermissionGroup, CorePikkuFunctionConfig, CorePikkuPermission } from './functions.types.js';
|
|
4
4
|
import type { SessionService } from '../services/user-session-service.js';
|
|
@@ -19,7 +19,7 @@ export declare const runPikkuFunc: <In = any, Out = any>(wireType: PikkuWiringTy
|
|
|
19
19
|
wirePermissions?: CorePermissionGroup | CorePikkuPermission[];
|
|
20
20
|
coerceDataFromSchema?: boolean;
|
|
21
21
|
tags?: string[];
|
|
22
|
-
wire:
|
|
22
|
+
wire: PikkuRawWire;
|
|
23
23
|
sessionService?: SessionService<CoreUserSession>;
|
|
24
24
|
credentialWireService?: PikkuCredentialWireService;
|
|
25
25
|
packageName?: string | null;
|
|
@@ -149,8 +149,12 @@ export type CorePikkuFunctionConfig<PikkuFunction extends CorePikkuFunction<any,
|
|
|
149
149
|
readonly?: boolean;
|
|
150
150
|
deploy?: 'serverless' | 'server' | 'auto';
|
|
151
151
|
approvalRequired?: boolean;
|
|
152
|
-
/** When
|
|
153
|
-
|
|
152
|
+
/** When true, workflow steps calling this function are dispatched via the queue. No queue service configured is a hard error. Defaults to false (inline). */
|
|
153
|
+
workflowQueued?: boolean;
|
|
154
|
+
/** Number of retry attempts when this function is used as a workflow step. */
|
|
155
|
+
workflowRetries?: number;
|
|
156
|
+
/** Timeout for this function when used as a workflow step (e.g. '30s', '5m'). */
|
|
157
|
+
workflowTimeout?: string;
|
|
154
158
|
audit?: boolean | {
|
|
155
159
|
durability?: 'best-effort' | 'transactional';
|
|
156
160
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module @pikku/core
|
|
3
3
|
*/
|
|
4
|
-
export type { CommonWireMeta, CoreConfig, CorePikkuMiddleware, CorePikkuMiddlewareConfig, CorePikkuMiddlewareFactory, CorePikkuMiddlewareGroup, CoreServices, CoreSingletonServices, CoreUserSession, CreateConfig, FunctionMeta, FunctionRuntimeMeta, FunctionServicesMeta, FunctionWiresMeta, FunctionsMeta, FunctionsRuntimeMeta, JSONPrimitive, JSONValue, MakeRequired, MiddlewareMetadata, MiddlewarePriority, PermissionMetadata, PickOptional, PickRequired, PikkuAIMiddlewareHooks, PikkuWire, PikkuWiringTypes, RequireAtLeastOne, SerializedError, WireServices, } from './types/core.types.js';
|
|
4
|
+
export type { CommonWireMeta, CoreConfig, CorePikkuMiddleware, CorePikkuMiddlewareConfig, CorePikkuMiddlewareFactory, CorePikkuMiddlewareGroup, CoreServices, CoreSingletonServices, CoreUserSession, CreateConfig, ServerLifecycle, FunctionMeta, FunctionRuntimeMeta, FunctionServicesMeta, FunctionWiresMeta, FunctionsMeta, FunctionsRuntimeMeta, JSONPrimitive, JSONValue, MakeRequired, MiddlewareMetadata, MiddlewarePriority, PermissionMetadata, PickOptional, PickRequired, PikkuAIMiddlewareHooks, PikkuWire, PikkuRawWire, PikkuWiringTypes, RequireAtLeastOne, SerializedError, WireServices, } from './types/core.types.js';
|
|
5
5
|
export { pikkuAIMiddleware, pikkuChannelMiddleware, pikkuChannelMiddlewareFactory, pikkuMiddleware, pikkuMiddlewareFactory, } from './types/core.types.js';
|
|
6
6
|
export type { CorePikkuAuth, CorePikkuAuthConfig, CorePikkuFunction, CorePikkuFunctionConfig, CorePikkuPermission, CorePikkuPermissionConfig, CorePikkuPermissionFactory, CorePikkuApprovalDescription, CorePermissionGroup, ZodLike, } from './function/functions.types.js';
|
|
7
7
|
export { pikkuAuth, pikkuPermission, pikkuPermissionFactory, pikkuApprovalDescription, } from './function/functions.types.js';
|
|
@@ -47,7 +47,7 @@ export type { WireAddonConfig } from './wirings/rpc/wire-addon.js';
|
|
|
47
47
|
export type { PikkuPackageState } from './types/state.types.js';
|
|
48
48
|
export { runMiddleware, addTagMiddleware, addTagMiddleware as addMiddleware, addGlobalMiddleware, } from './middleware-runner.js';
|
|
49
49
|
export { addTagPermission, addTagPermission as addPermission, addGlobalPermission, checkAuthPermissions, } from './permissions.js';
|
|
50
|
-
export { isSerializable, stopSingletonServices } from './utils.js';
|
|
50
|
+
export { isSerializable, stopSingletonServices, pikkuServerLifecycle, } from './utils.js';
|
|
51
51
|
export { getSingletonServices, getCreateWireServices, setSingletonServices, } from './pikku-state.js';
|
|
52
52
|
export { clearPikkuRuntimeState } from './test-utils.js';
|
|
53
53
|
export { type ScheduledTaskInfo, type ScheduledTaskSummary, } from './services/scheduler-service.js';
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@ export { createGraph } from './wirings/workflow/graph/graph-node.js';
|
|
|
17
17
|
export { wireAddon } from './wirings/rpc/wire-addon.js';
|
|
18
18
|
export { runMiddleware, addTagMiddleware, addTagMiddleware as addMiddleware, addGlobalMiddleware, } from './middleware-runner.js';
|
|
19
19
|
export { addTagPermission, addTagPermission as addPermission, addGlobalPermission, checkAuthPermissions, } from './permissions.js';
|
|
20
|
-
export { isSerializable, stopSingletonServices } from './utils.js';
|
|
20
|
+
export { isSerializable, stopSingletonServices, pikkuServerLifecycle, } from './utils.js';
|
|
21
21
|
export { getSingletonServices, getCreateWireServices, setSingletonServices, } from './pikku-state.js';
|
|
22
22
|
export { clearPikkuRuntimeState } from './test-utils.js';
|
|
23
23
|
export { SchedulerService } from './services/scheduler-service.js';
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { CredentialService } from './credential-service.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { PikkuRawWire } from '../types/core.types.js';
|
|
3
3
|
export declare class PikkuCredentialWireService {
|
|
4
4
|
private credentialService?;
|
|
5
5
|
private wire?;
|
|
6
6
|
private credentials;
|
|
7
7
|
private loaded;
|
|
8
8
|
private loadPromise;
|
|
9
|
-
constructor(credentialService?: CredentialService | undefined, wire?:
|
|
9
|
+
constructor(credentialService?: CredentialService | undefined, wire?: PikkuRawWire | undefined);
|
|
10
10
|
set(name: string, value: unknown): void;
|
|
11
11
|
get<T = unknown>(name: string): T | null | Promise<T | null>;
|
|
12
12
|
getAll(): Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
@@ -17,6 +17,7 @@ import type { WorkflowPlannedStep, WorkflowRun, WorkflowRunService, WorkflowRunW
|
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
19
19
|
export declare class InMemoryWorkflowService extends PikkuWorkflowService implements WorkflowRunService {
|
|
20
|
+
constructor();
|
|
20
21
|
private sleepTimers;
|
|
21
22
|
private runs;
|
|
22
23
|
private steps;
|
|
@@ -17,6 +17,9 @@ import { isExpectedError } from '../errors/error-handler.js';
|
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
19
19
|
export class InMemoryWorkflowService extends PikkuWorkflowService {
|
|
20
|
+
constructor() {
|
|
21
|
+
super({ wireQueues: false });
|
|
22
|
+
}
|
|
20
23
|
sleepTimers = new Set();
|
|
21
24
|
runs = new Map();
|
|
22
25
|
steps = new Map(); // keyed by `${runId}:${stepName}`
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export type PikkuUserIdResolver = (wire:
|
|
1
|
+
import type { PikkuRawWire } from '../types/core.types.js';
|
|
2
|
+
export type PikkuUserIdResolver = (wire: PikkuRawWire) => string | undefined;
|
|
3
3
|
export declare const defaultPikkuUserIdResolver: PikkuUserIdResolver;
|
|
@@ -6,6 +6,7 @@ import type { RunTimeline, ReconstructedRunState } from '../wirings/workflow/run
|
|
|
6
6
|
* Handles workflow execution, replay, orchestration logic, and run-level state
|
|
7
7
|
*/
|
|
8
8
|
export interface WorkflowService {
|
|
9
|
+
wireQueueWorkers?(): void;
|
|
9
10
|
createRun(workflowName: string, input: any, inline: boolean, graphHash: string, wire: WorkflowRunWire, options?: {
|
|
10
11
|
deterministic?: boolean;
|
|
11
12
|
plannedSteps?: WorkflowPlannedStep[];
|
|
@@ -82,8 +82,12 @@ export type FunctionRuntimeMeta = {
|
|
|
82
82
|
readonly?: boolean;
|
|
83
83
|
deploy?: 'serverless' | 'server' | 'auto';
|
|
84
84
|
sessionless?: boolean;
|
|
85
|
-
/** When
|
|
86
|
-
|
|
85
|
+
/** When true, workflow steps calling this function are dispatched via the queue. No queue service configured is a hard error. */
|
|
86
|
+
workflowQueued?: boolean;
|
|
87
|
+
/** Retry count when this function is used as a workflow step. */
|
|
88
|
+
workflowRetries?: number;
|
|
89
|
+
/** Timeout when this function is used as a workflow step (e.g. '30s', '5m'). */
|
|
90
|
+
workflowTimeout?: string;
|
|
87
91
|
version?: number;
|
|
88
92
|
approvalRequired?: boolean;
|
|
89
93
|
approvalDescription?: string;
|
|
@@ -122,7 +126,7 @@ export type JSONValue = JSONPrimitive | JSONValue[] | {
|
|
|
122
126
|
/**
|
|
123
127
|
* Utility type for making certain keys required and leaving the rest as optional.
|
|
124
128
|
*/
|
|
125
|
-
export type PickRequired<T, K extends keyof T> = Required<Pick<T, K
|
|
129
|
+
export type PickRequired<T, K extends keyof T> = T & Required<Pick<T, K>>;
|
|
126
130
|
/**
|
|
127
131
|
* Utility type for making certain keys optional while keeping the rest required.
|
|
128
132
|
*/
|
|
@@ -210,7 +214,10 @@ export interface CoreSingletonServices<Config extends CoreConfig = CoreConfig> {
|
|
|
210
214
|
/**
|
|
211
215
|
* Represents different forms of wire within Pikku and the outside world.
|
|
212
216
|
*/
|
|
213
|
-
export type PikkuWire<In = unknown, Out = unknown, HasInitialSession extends boolean = false, UserSession extends CoreUserSession = CoreUserSession, TypedRPC extends PikkuRPC = PikkuRPC, IsChannel extends true | null = null, MCPTools extends string | never = never, TypedWorkflow extends PikkuWorkflowWire | never = PikkuWorkflowWire, TriggerOutput = unknown> =
|
|
217
|
+
export type PikkuWire<In = unknown, Out = unknown, HasInitialSession extends boolean = false, UserSession extends CoreUserSession = CoreUserSession, TypedRPC extends PikkuRPC = PikkuRPC, IsChannel extends true | null = null, MCPTools extends string | never = never, TypedWorkflow extends PikkuWorkflowWire | never = PikkuWorkflowWire, TriggerOutput = unknown> = {
|
|
218
|
+
/** Always present — lazily initialised on first access for every function invocation */
|
|
219
|
+
rpc: TypedRPC;
|
|
220
|
+
} & Partial<{
|
|
214
221
|
wireType: PikkuWiringTypes;
|
|
215
222
|
wireId: string;
|
|
216
223
|
/** Trace ID for distributed tracing — propagated across remote RPC calls via x-trace-id header */
|
|
@@ -219,7 +226,6 @@ export type PikkuWire<In = unknown, Out = unknown, HasInitialSession extends boo
|
|
|
219
226
|
functionId: string;
|
|
220
227
|
http: PikkuHTTP<In>;
|
|
221
228
|
mcp: PikkuMCP<MCPTools>;
|
|
222
|
-
rpc: TypedRPC;
|
|
223
229
|
channel: [IsChannel] extends [null] ? PikkuChannel<unknown, Out> : PikkuChannel<unknown, Out> | undefined;
|
|
224
230
|
scheduledTask: PikkuScheduledTask;
|
|
225
231
|
queue: PikkuQueue;
|
|
@@ -251,10 +257,14 @@ export type PikkuWire<In = unknown, Out = unknown, HasInitialSession extends boo
|
|
|
251
257
|
durability: AuditDurability;
|
|
252
258
|
};
|
|
253
259
|
}>;
|
|
260
|
+
/**
|
|
261
|
+
* Wire object as constructed by runners, before rpc is lazily added by the function runner.
|
|
262
|
+
*/
|
|
263
|
+
export type PikkuRawWire = Omit<PikkuWire, 'rpc'>;
|
|
254
264
|
/**
|
|
255
265
|
* A function that can wrap an wire and be called before or after
|
|
256
266
|
*/
|
|
257
|
-
export type CorePikkuMiddleware<SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession> = (services: SingletonServices, wires: PikkuWire
|
|
267
|
+
export type CorePikkuMiddleware<SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession> = (services: SingletonServices, wires: PikkuWire, next: () => Promise<void>) => Promise<void>;
|
|
258
268
|
/**
|
|
259
269
|
* Priority levels for middleware execution order.
|
|
260
270
|
* Lower priority runs first (outermost in the onion model).
|
|
@@ -352,11 +362,18 @@ export type CreateSingletonServices<Config extends CoreConfig, SingletonServices
|
|
|
352
362
|
/**
|
|
353
363
|
* Defines a function type for creating session-specific services.
|
|
354
364
|
*/
|
|
355
|
-
export type CreateWireServices<SingletonServices extends CoreSingletonServices = CoreSingletonServices, Services extends CoreServices<SingletonServices> = CoreServices<SingletonServices>, UserSession extends CoreUserSession = CoreUserSession> = (services: SingletonServices, wire:
|
|
365
|
+
export type CreateWireServices<SingletonServices extends CoreSingletonServices = CoreSingletonServices, Services extends CoreServices<SingletonServices> = CoreServices<SingletonServices>, UserSession extends CoreUserSession = CoreUserSession> = (services: SingletonServices, wire: PikkuRawWire) => Promise<WireServices<Services, SingletonServices>>;
|
|
356
366
|
/**
|
|
357
367
|
* Defines a function type for creating config.
|
|
358
368
|
*/
|
|
359
369
|
export type CreateConfig<Config extends CoreConfig, RemainingArgs extends any[] = unknown[]> = (variables?: VariablesService, ...args: RemainingArgs) => Promise<Config>;
|
|
370
|
+
/** Server lifecycle hooks for startup and shutdown phases. */
|
|
371
|
+
export type ServerLifecycle<SingletonServices extends CoreSingletonServices = CoreSingletonServices> = {
|
|
372
|
+
beforeStart?: (services: SingletonServices) => void | Promise<void>;
|
|
373
|
+
afterStart?: (services: SingletonServices) => void | Promise<void>;
|
|
374
|
+
beforeStop?: (services: SingletonServices) => void | Promise<void>;
|
|
375
|
+
afterStop?: (services: SingletonServices) => void | Promise<void>;
|
|
376
|
+
};
|
|
360
377
|
/**
|
|
361
378
|
* Represents the documentation for a route, including summary, description, tags, and errors.
|
|
362
379
|
*/
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Logger } from './services/logger.js';
|
|
2
|
-
import type { WireServices } from './types/core.types.js';
|
|
2
|
+
import type { CoreSingletonServices, ServerLifecycle, WireServices } from './types/core.types.js';
|
|
3
3
|
export declare const closeWireServices: (logger: Logger, wireServices: WireServices) => Promise<void>;
|
|
4
4
|
export declare const createWeakUID: () => string;
|
|
5
5
|
export declare const isSerializable: (data: any) => boolean;
|
|
@@ -12,3 +12,5 @@ export declare const freezeDedupe: <T>(arr?: readonly T[] | T[] | undefined) =>
|
|
|
12
12
|
* @param singletonServices - The parent singleton services to stop
|
|
13
13
|
*/
|
|
14
14
|
export declare const stopSingletonServices: () => Promise<void>;
|
|
15
|
+
/** Wrap server lifecycle hooks so the inspector can discover them. */
|
|
16
|
+
export declare const pikkuServerLifecycle: <SS extends CoreSingletonServices = CoreSingletonServices>(lifecycle: ServerLifecycle<SS>) => ServerLifecycle<SS>;
|
package/dist/utils.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CoreSingletonServices, MiddlewareMetadata,
|
|
1
|
+
import type { CoreSingletonServices, MiddlewareMetadata, PikkuRawWire } from '../../types/core.types.js';
|
|
2
2
|
import type { CorePikkuChannelMiddleware } from './channel.types.js';
|
|
3
3
|
export declare const addChannelMiddleware: (tag: string, middleware: CorePikkuChannelMiddleware[], packageName?: string | null) => CorePikkuChannelMiddleware[];
|
|
4
4
|
export declare const clearChannelMiddlewareCache: () => void;
|
|
@@ -7,4 +7,4 @@ export declare const combineChannelMiddleware: (wireType: string, uid: string, {
|
|
|
7
7
|
wireChannelMiddleware?: CorePikkuChannelMiddleware[];
|
|
8
8
|
packageName?: string | null;
|
|
9
9
|
}) => readonly CorePikkuChannelMiddleware[];
|
|
10
|
-
export declare function wrapChannelWithMiddleware<Out>(wire:
|
|
10
|
+
export declare function wrapChannelWithMiddleware<Out>(wire: PikkuRawWire, services: CoreSingletonServices, middlewares: readonly CorePikkuChannelMiddleware[]): PikkuRawWire;
|
|
@@ -50,10 +50,13 @@ export class LocalEventHubService {
|
|
|
50
50
|
continue; // Skip sending to the sender
|
|
51
51
|
const channel = this.channels.get(toChannelId);
|
|
52
52
|
if (channel) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
try {
|
|
54
|
+
channel.send(data, isBinary);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Channel closed (e.g. SSE ReadableStream controller already closed on browser disconnect) — clean up
|
|
58
|
+
this.onChannelClosed(toChannelId);
|
|
59
|
+
}
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CoreServices,
|
|
1
|
+
import type { CoreServices, PikkuRawWire } from '../../types/core.types.js';
|
|
2
2
|
import type { SessionService } from '../../services/user-session-service.js';
|
|
3
3
|
import type { CoreUserSession } from '../../types/core.types.js';
|
|
4
4
|
import { PikkuError } from '../../errors/error-handler.js';
|
|
@@ -19,7 +19,7 @@ export declare class ContextAwareRPCService {
|
|
|
19
19
|
private wire;
|
|
20
20
|
private options;
|
|
21
21
|
private packageName;
|
|
22
|
-
constructor(services: CoreServices, wire:
|
|
22
|
+
constructor(services: CoreServices, wire: PikkuRawWire, options: {
|
|
23
23
|
requiresAuth?: boolean;
|
|
24
24
|
sessionService?: SessionService<CoreUserSession>;
|
|
25
25
|
}, packageName?: string | null);
|
|
@@ -32,7 +32,7 @@ export declare class ContextAwareRPCService {
|
|
|
32
32
|
* @private
|
|
33
33
|
*/
|
|
34
34
|
private invokeAddonFunction;
|
|
35
|
-
rpcWithWire<In = any, Out = any>(rpcName: string, data: In, wire:
|
|
35
|
+
rpcWithWire<In = any, Out = any>(rpcName: string, data: In, wire: PikkuRawWire): Promise<Out>;
|
|
36
36
|
startWorkflow<In = any>(workflowName: string, input: In, options?: {
|
|
37
37
|
startNode?: string;
|
|
38
38
|
wire?: {
|
|
@@ -94,7 +94,7 @@ export declare class ContextAwareRPCService {
|
|
|
94
94
|
remote<In = any, Out = any>(funcName: string, data: In): Promise<Out>;
|
|
95
95
|
}
|
|
96
96
|
export declare class PikkuRPCService<Services extends CoreServices, TypedRPC = PikkuRPC> {
|
|
97
|
-
getContextRPCService(services: Services, wire:
|
|
97
|
+
getContextRPCService(services: Services, wire: PikkuRawWire, requiresAuthOrOptions?: boolean | {
|
|
98
98
|
requiresAuth?: boolean;
|
|
99
99
|
sessionService?: SessionService<CoreUserSession>;
|
|
100
100
|
} | undefined, depth?: number, packageName?: string | null): TypedRPC;
|
|
@@ -110,16 +110,8 @@ export class ContextAwareRPCService {
|
|
|
110
110
|
return await this.rpc(funcName, data);
|
|
111
111
|
}
|
|
112
112
|
async rpc(funcName, data) {
|
|
113
|
-
const rpcDepth = this.wire.rpc?.depth || 0;
|
|
114
113
|
const updatedWire = {
|
|
115
114
|
...this.wire,
|
|
116
|
-
rpc: this.wire.rpc
|
|
117
|
-
? {
|
|
118
|
-
...this.wire.rpc,
|
|
119
|
-
depth: rpcDepth + 1,
|
|
120
|
-
global: false,
|
|
121
|
-
}
|
|
122
|
-
: undefined,
|
|
123
115
|
};
|
|
124
116
|
// Check addon namespace first (e.g. 'stripe:createCharge')
|
|
125
117
|
if (funcName.includes(':')) {
|
|
@@ -197,17 +189,9 @@ export class ContextAwareRPCService {
|
|
|
197
189
|
});
|
|
198
190
|
}
|
|
199
191
|
async rpcWithWire(rpcName, data, wire) {
|
|
200
|
-
const rpcDepth = this.wire.rpc?.depth || 0;
|
|
201
192
|
const mergedWire = {
|
|
202
193
|
...this.wire,
|
|
203
194
|
...wire,
|
|
204
|
-
rpc: this.wire.rpc
|
|
205
|
-
? {
|
|
206
|
-
...this.wire.rpc,
|
|
207
|
-
depth: rpcDepth + 1,
|
|
208
|
-
global: false,
|
|
209
|
-
}
|
|
210
|
-
: undefined,
|
|
211
195
|
};
|
|
212
196
|
if (rpcName.includes(':')) {
|
|
213
197
|
return this.invokeAddonFunction(rpcName, data, mergedWire);
|
|
@@ -66,6 +66,9 @@ export declare class WorkflowRunFailedError extends PikkuError {
|
|
|
66
66
|
};
|
|
67
67
|
constructor(message?: string);
|
|
68
68
|
}
|
|
69
|
+
export declare class WorkflowRunCancelledError extends PikkuError {
|
|
70
|
+
constructor();
|
|
71
|
+
}
|
|
69
72
|
export declare class WorkflowServiceNotInitialized extends Error {
|
|
70
73
|
}
|
|
71
74
|
export declare class WorkflowStepNameNotString extends Error {
|
|
@@ -85,13 +88,13 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
85
88
|
mirror?: WorkflowRunMirror;
|
|
86
89
|
});
|
|
87
90
|
private safeMirror;
|
|
88
|
-
rewireQueueWorkers(): void;
|
|
89
91
|
/**
|
|
90
92
|
* Wire the queue-based orchestrator/step/sleeper workers.
|
|
91
93
|
* Subclasses that orchestrate without queues (e.g. Durable Objects) should
|
|
92
94
|
* pass `wireQueues: false` to the base constructor and skip this entirely.
|
|
95
|
+
* Call this explicitly after adding addons dynamically.
|
|
93
96
|
*/
|
|
94
|
-
|
|
97
|
+
wireQueueWorkers(): void;
|
|
95
98
|
/**
|
|
96
99
|
* Check if a run is executing inline (without queues)
|
|
97
100
|
*/
|
|
@@ -131,6 +131,15 @@ addError(WorkflowRunFailedError, {
|
|
|
131
131
|
status: 422,
|
|
132
132
|
message: 'Workflow run failed.',
|
|
133
133
|
});
|
|
134
|
+
export class WorkflowRunCancelledError extends PikkuError {
|
|
135
|
+
constructor() {
|
|
136
|
+
super('Workflow was cancelled');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
addError(WorkflowRunCancelledError, {
|
|
140
|
+
status: 409,
|
|
141
|
+
message: 'Workflow was cancelled.',
|
|
142
|
+
});
|
|
134
143
|
export class WorkflowServiceNotInitialized extends Error {
|
|
135
144
|
}
|
|
136
145
|
export class WorkflowStepNameNotString extends Error {
|
|
@@ -177,13 +186,11 @@ export class PikkuWorkflowService {
|
|
|
177
186
|
}
|
|
178
187
|
}
|
|
179
188
|
}
|
|
180
|
-
rewireQueueWorkers() {
|
|
181
|
-
this.wireQueueWorkers();
|
|
182
|
-
}
|
|
183
189
|
/**
|
|
184
190
|
* Wire the queue-based orchestrator/step/sleeper workers.
|
|
185
191
|
* Subclasses that orchestrate without queues (e.g. Durable Objects) should
|
|
186
192
|
* pass `wireQueues: false` to the base constructor and skip this entirely.
|
|
193
|
+
* Call this explicitly after adding addons dynamically.
|
|
187
194
|
*/
|
|
188
195
|
wireQueueWorkers() {
|
|
189
196
|
const functions = pikkuState(null, 'function', 'functions');
|
|
@@ -548,26 +555,19 @@ export class PikkuWorkflowService {
|
|
|
548
555
|
* through to the inline execution path.
|
|
549
556
|
*/
|
|
550
557
|
async dispatchStep(runId, stepName, rpcName, data, stepOptions, fromStepName) {
|
|
551
|
-
// Step execution is decided purely by the function's `
|
|
552
|
-
//
|
|
553
|
-
// the queue.
|
|
554
|
-
//
|
|
555
|
-
// normally-started workflow executes its steps in one orchestrator-worker
|
|
556
|
-
// pass instead of one queue round-trip per step.
|
|
558
|
+
// Step execution is decided purely by the function's `workflowQueued` flag
|
|
559
|
+
// (default false). Only a function explicitly marked `workflowQueued: true`
|
|
560
|
+
// dispatches via the queue. If the queue service is not configured that is
|
|
561
|
+
// a hard error — there is no inline fallback.
|
|
557
562
|
const functionsMeta = pikkuState(null, 'function', 'meta');
|
|
558
563
|
const rpcFuncId = pikkuState(null, 'rpc', 'meta')[rpcName];
|
|
559
564
|
const rpcMeta = typeof rpcFuncId === 'string' ? functionsMeta[rpcFuncId] : undefined;
|
|
560
|
-
const forceQueue = rpcMeta?.
|
|
565
|
+
const forceQueue = rpcMeta?.workflowQueued === true;
|
|
561
566
|
if (!forceQueue) {
|
|
562
567
|
return false;
|
|
563
568
|
}
|
|
564
|
-
// The function opted out of inline execution (`inline: false`) but no queue
|
|
565
|
-
// service is configured to dispatch it. Fall back to inline so the workflow
|
|
566
|
-
// still progresses, but warn loudly — silently swallowing this hides a real
|
|
567
|
-
// misconfiguration (the step won't get its own worker/retry isolation).
|
|
568
569
|
if (!getSingletonServices()?.queueService) {
|
|
569
|
-
|
|
570
|
-
return false;
|
|
570
|
+
throw new Error(`Workflow step '${stepName}' (function '${rpcName}') is marked 'workflowQueued: true' but no queue service is configured.`);
|
|
571
571
|
}
|
|
572
572
|
try {
|
|
573
573
|
await getSingletonServices().queueService.add(this.getStepWorkerQueueName(rpcName), JSON.parse(JSON.stringify({ runId, stepName, rpcName, data, fromStepName })), this.resolveStepJobOptions(stepOptions));
|
|
@@ -690,7 +690,7 @@ export class PikkuWorkflowService {
|
|
|
690
690
|
throw new WorkflowRunFailedError(run.error?.message);
|
|
691
691
|
}
|
|
692
692
|
if (run.status === 'cancelled') {
|
|
693
|
-
throw new
|
|
693
|
+
throw new WorkflowRunCancelledError();
|
|
694
694
|
}
|
|
695
695
|
return run.output;
|
|
696
696
|
}
|
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
CorePikkuMiddleware,
|
|
12
12
|
PikkuWiringTypes,
|
|
13
13
|
PikkuWire,
|
|
14
|
+
PikkuRawWire,
|
|
14
15
|
MiddlewareMetadata,
|
|
15
16
|
PermissionMetadata,
|
|
16
17
|
CoreSingletonServices,
|
|
@@ -37,7 +38,7 @@ import { rpcService } from '../wirings/rpc/rpc-runner.js'
|
|
|
37
38
|
import { closeWireServices } from '../utils.js'
|
|
38
39
|
|
|
39
40
|
async function resolveSession(
|
|
40
|
-
wire:
|
|
41
|
+
wire: PikkuRawWire,
|
|
41
42
|
singletonServices: CoreSingletonServices,
|
|
42
43
|
sessionService?: SessionService<CoreUserSession>
|
|
43
44
|
): Promise<void> {
|
|
@@ -234,7 +235,7 @@ export const runPikkuFunc = async <In = any, Out = any>(
|
|
|
234
235
|
wirePermissions?: CorePermissionGroup | CorePikkuPermission[]
|
|
235
236
|
coerceDataFromSchema?: boolean
|
|
236
237
|
tags?: string[]
|
|
237
|
-
wire:
|
|
238
|
+
wire: PikkuRawWire
|
|
238
239
|
sessionService?: SessionService<CoreUserSession>
|
|
239
240
|
credentialWireService?: PikkuCredentialWireService
|
|
240
241
|
packageName?: string | null
|
|
@@ -295,8 +295,12 @@ export type CorePikkuFunctionConfig<
|
|
|
295
295
|
readonly?: boolean
|
|
296
296
|
deploy?: 'serverless' | 'server' | 'auto'
|
|
297
297
|
approvalRequired?: boolean
|
|
298
|
-
/** When
|
|
299
|
-
|
|
298
|
+
/** When true, workflow steps calling this function are dispatched via the queue. No queue service configured is a hard error. Defaults to false (inline). */
|
|
299
|
+
workflowQueued?: boolean
|
|
300
|
+
/** Number of retry attempts when this function is used as a workflow step. */
|
|
301
|
+
workflowRetries?: number
|
|
302
|
+
/** Timeout for this function when used as a workflow step (e.g. '30s', '5m'). */
|
|
303
|
+
workflowTimeout?: string
|
|
300
304
|
audit?:
|
|
301
305
|
| boolean
|
|
302
306
|
| {
|
package/src/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ export type {
|
|
|
12
12
|
CoreSingletonServices,
|
|
13
13
|
CoreUserSession,
|
|
14
14
|
CreateConfig,
|
|
15
|
+
ServerLifecycle,
|
|
15
16
|
FunctionMeta,
|
|
16
17
|
FunctionRuntimeMeta,
|
|
17
18
|
FunctionServicesMeta,
|
|
@@ -28,6 +29,7 @@ export type {
|
|
|
28
29
|
PickRequired,
|
|
29
30
|
PikkuAIMiddlewareHooks,
|
|
30
31
|
PikkuWire,
|
|
32
|
+
PikkuRawWire,
|
|
31
33
|
PikkuWiringTypes,
|
|
32
34
|
RequireAtLeastOne,
|
|
33
35
|
SerializedError,
|
|
@@ -187,7 +189,11 @@ export {
|
|
|
187
189
|
addGlobalPermission,
|
|
188
190
|
checkAuthPermissions,
|
|
189
191
|
} from './permissions.js'
|
|
190
|
-
export {
|
|
192
|
+
export {
|
|
193
|
+
isSerializable,
|
|
194
|
+
stopSingletonServices,
|
|
195
|
+
pikkuServerLifecycle,
|
|
196
|
+
} from './utils.js'
|
|
191
197
|
export {
|
|
192
198
|
getSingletonServices,
|
|
193
199
|
getCreateWireServices,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CredentialService } from './credential-service.js'
|
|
2
2
|
import { defaultPikkuUserIdResolver } from './pikku-user-id.js'
|
|
3
|
-
import type {
|
|
3
|
+
import type { PikkuRawWire } from '../types/core.types.js'
|
|
4
4
|
|
|
5
5
|
export class PikkuCredentialWireService {
|
|
6
6
|
private credentials: Record<string, unknown> = {}
|
|
@@ -9,7 +9,7 @@ export class PikkuCredentialWireService {
|
|
|
9
9
|
|
|
10
10
|
constructor(
|
|
11
11
|
private credentialService?: CredentialService,
|
|
12
|
-
private wire?:
|
|
12
|
+
private wire?: PikkuRawWire
|
|
13
13
|
) {}
|
|
14
14
|
|
|
15
15
|
set(name: string, value: unknown): void {
|
|
@@ -366,7 +366,7 @@ describe('InMemoryWorkflowService', () => {
|
|
|
366
366
|
})
|
|
367
367
|
|
|
368
368
|
describe('queue worker registration', () => {
|
|
369
|
-
test('
|
|
369
|
+
test('wireQueueWorkers registers workflow queues added after construction', () => {
|
|
370
370
|
const stepQueue = 'wf-step-regression-enrich'
|
|
371
371
|
const orchQueue = 'wf-orchestrator-regression-onboarding'
|
|
372
372
|
|
|
@@ -385,7 +385,7 @@ describe('InMemoryWorkflowService', () => {
|
|
|
385
385
|
assert.strictEqual(getQueueWorkers().has(stepQueue), false)
|
|
386
386
|
assert.strictEqual(getQueueWorkers().has(orchQueue), false)
|
|
387
387
|
|
|
388
|
-
ws.
|
|
388
|
+
ws.wireQueueWorkers()
|
|
389
389
|
|
|
390
390
|
assert.strictEqual(getQueueWorkers().has(stepQueue), true)
|
|
391
391
|
assert.strictEqual(getQueueWorkers().has(orchQueue), true)
|
|
@@ -39,6 +39,10 @@ export class InMemoryWorkflowService
|
|
|
39
39
|
extends PikkuWorkflowService
|
|
40
40
|
implements WorkflowRunService
|
|
41
41
|
{
|
|
42
|
+
constructor() {
|
|
43
|
+
super({ wireQueues: false })
|
|
44
|
+
}
|
|
45
|
+
|
|
42
46
|
private sleepTimers = new Set<ReturnType<typeof setTimeout>>()
|
|
43
47
|
private runs = new Map<string, WorkflowRun>()
|
|
44
48
|
private steps = new Map<string, StepState>() // keyed by `${runId}:${stepName}`
|
|
@@ -451,7 +455,9 @@ export class InMemoryWorkflowService
|
|
|
451
455
|
return nodeIds.filter((id) => !existingSteps.has(id))
|
|
452
456
|
}
|
|
453
457
|
|
|
454
|
-
async getStepInstances(
|
|
458
|
+
async getStepInstances(
|
|
459
|
+
runId: string
|
|
460
|
+
): Promise<
|
|
455
461
|
Array<{ stepName: string; status: StepStatus; fromStepName?: string }>
|
|
456
462
|
> {
|
|
457
463
|
const prefix = `${runId}:`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PikkuRawWire } from '../types/core.types.js'
|
|
2
2
|
|
|
3
|
-
export type PikkuUserIdResolver = (wire:
|
|
3
|
+
export type PikkuUserIdResolver = (wire: PikkuRawWire) => string | undefined
|
|
4
4
|
|
|
5
5
|
export const defaultPikkuUserIdResolver: PikkuUserIdResolver = (wire) => {
|
|
6
6
|
// Explicit pikkuUserId on wire (set by earlier middleware or runner)
|