@pikku/core 0.12.40 → 0.12.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/function/function-runner.d.ts +2 -2
  3. package/dist/index.d.ts +2 -2
  4. package/dist/index.js +1 -1
  5. package/dist/services/credential-wire-service.d.ts +2 -2
  6. package/dist/services/in-memory-workflow-service.d.ts +1 -0
  7. package/dist/services/in-memory-workflow-service.js +3 -0
  8. package/dist/services/pikku-user-id.d.ts +2 -2
  9. package/dist/services/workflow-service.d.ts +1 -0
  10. package/dist/types/core.types.d.ts +18 -5
  11. package/dist/utils.d.ts +3 -1
  12. package/dist/utils.js +2 -0
  13. package/dist/wirings/channel/channel-middleware-runner.d.ts +2 -2
  14. package/dist/wirings/channel/local/local-eventhub-service.js +7 -4
  15. package/dist/wirings/rpc/rpc-runner.d.ts +4 -4
  16. package/dist/wirings/rpc/rpc-runner.js +0 -16
  17. package/dist/wirings/workflow/dsl/index.d.ts +1 -1
  18. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +9 -1
  19. package/dist/wirings/workflow/index.d.ts +1 -1
  20. package/dist/wirings/workflow/pikku-workflow-service.d.ts +5 -2
  21. package/dist/wirings/workflow/pikku-workflow-service.js +11 -4
  22. package/dist/wirings/workflow/workflow.types.d.ts +4 -2
  23. package/package.json +1 -1
  24. package/src/function/function-runner.ts +3 -2
  25. package/src/index.ts +7 -1
  26. package/src/services/credential-wire-service.ts +2 -2
  27. package/src/services/in-memory-workflow-service.test.ts +2 -2
  28. package/src/services/in-memory-workflow-service.ts +7 -1
  29. package/src/services/pikku-user-id.ts +2 -2
  30. package/src/services/workflow-service.ts +1 -0
  31. package/src/types/core.types.ts +22 -6
  32. package/src/utils.ts +12 -1
  33. package/src/wirings/ai-agent/ai-agent-prepare.ts +2 -2
  34. package/src/wirings/channel/channel-common.ts +8 -2
  35. package/src/wirings/channel/channel-middleware-runner.ts +3 -3
  36. package/src/wirings/channel/local/local-channel-runner.ts +7 -3
  37. package/src/wirings/channel/local/local-eventhub-service.ts +6 -3
  38. package/src/wirings/channel/serverless/serverless-channel-runner.ts +4 -4
  39. package/src/wirings/cli/cli-runner.ts +2 -2
  40. package/src/wirings/gateway/gateway-runner.ts +8 -2
  41. package/src/wirings/http/http-runner.ts +3 -2
  42. package/src/wirings/mcp/mcp-runner.ts +2 -2
  43. package/src/wirings/queue/queue-runner.ts +2 -2
  44. package/src/wirings/rpc/rpc-runner.ts +12 -24
  45. package/src/wirings/scheduler/scheduler-runner.ts +2 -2
  46. package/src/wirings/trigger/trigger-runner.ts +2 -2
  47. package/src/wirings/workflow/dsl/index.ts +1 -0
  48. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +10 -0
  49. package/src/wirings/workflow/index.ts +1 -0
  50. package/src/wirings/workflow/pikku-workflow-service.test.ts +3 -2
  51. package/src/wirings/workflow/pikku-workflow-service.ts +29 -10
  52. package/src/wirings/workflow/workflow.types.ts +4 -1
  53. package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,32 @@
1
+ ## 0.12.42
2
+
3
+ ### Patch Changes
4
+
5
+ - 7b5b10a: fix(workflow): include suspend steps in plannedSteps with readable displayName
6
+
7
+ `workflow.suspend(reason)` calls now appear in the static `plannedSteps` ladder
8
+ produced by `deriveWorkflowPlan`. Previously the inspector ignored them, so the
9
+ runtime's `__workflow_suspend:<reason>` steps had no planned counterpart and
10
+ the UI appended them as orphans at the bottom of the step list instead of
11
+ showing them at the correct position.
12
+
13
+ Changes:
14
+ - `WorkflowPlannedStep` gains an optional `displayName` field — the human-
15
+ readable label to show in the UI (falls back to `stepName` when absent).
16
+ - New `SuspendStepMeta` type added to `WorkflowStepMeta`.
17
+ - Inspector extracts `workflow.suspend('reason')` calls and emits a
18
+ `SuspendStepMeta` step with `type: 'suspend'` and `reason`.
19
+ - `collectNamedSteps` maps a suspend step to
20
+ `{ stepName: '__workflow_suspend:<reason>', displayName: '<reason>' }`,
21
+ matching the key the runtime stores so the UI can overlay live status
22
+ onto the planned position.
23
+
24
+ ## 0.12.41
25
+
26
+ ### Patch Changes
27
+
28
+ - 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.
29
+
1
30
  ## 0.12.40
2
31
 
3
32
  ### Patch Changes
@@ -1,4 +1,4 @@
1
- import type { CoreUserSession, CorePikkuMiddleware, PikkuWiringTypes, PikkuWire, MiddlewareMetadata, PermissionMetadata, CoreSingletonServices, CreateWireServices } from '../types/core.types.js';
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: PikkuWire;
22
+ wire: PikkuRawWire;
23
23
  sessionService?: SessionService<CoreUserSession>;
24
24
  credentialWireService?: PikkuCredentialWireService;
25
25
  packageName?: string | null;
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 { PikkuWire } from '../types/core.types.js';
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?: PikkuWire | undefined);
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 { PikkuWire } from '../types/core.types.js';
2
- export type PikkuUserIdResolver = (wire: PikkuWire) => string | undefined;
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[];
@@ -126,7 +126,7 @@ export type JSONValue = JSONPrimitive | JSONValue[] | {
126
126
  /**
127
127
  * Utility type for making certain keys required and leaving the rest as optional.
128
128
  */
129
- export type PickRequired<T, K extends keyof T> = Required<Pick<T, K>> & Partial<T>;
129
+ export type PickRequired<T, K extends keyof T> = T & Required<Pick<T, K>>;
130
130
  /**
131
131
  * Utility type for making certain keys optional while keeping the rest required.
132
132
  */
@@ -214,7 +214,10 @@ export interface CoreSingletonServices<Config extends CoreConfig = CoreConfig> {
214
214
  /**
215
215
  * Represents different forms of wire within Pikku and the outside world.
216
216
  */
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> = Partial<{
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<{
218
221
  wireType: PikkuWiringTypes;
219
222
  wireId: string;
220
223
  /** Trace ID for distributed tracing — propagated across remote RPC calls via x-trace-id header */
@@ -223,7 +226,6 @@ export type PikkuWire<In = unknown, Out = unknown, HasInitialSession extends boo
223
226
  functionId: string;
224
227
  http: PikkuHTTP<In>;
225
228
  mcp: PikkuMCP<MCPTools>;
226
- rpc: TypedRPC;
227
229
  channel: [IsChannel] extends [null] ? PikkuChannel<unknown, Out> : PikkuChannel<unknown, Out> | undefined;
228
230
  scheduledTask: PikkuScheduledTask;
229
231
  queue: PikkuQueue;
@@ -255,10 +257,14 @@ export type PikkuWire<In = unknown, Out = unknown, HasInitialSession extends boo
255
257
  durability: AuditDurability;
256
258
  };
257
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'>;
258
264
  /**
259
265
  * A function that can wrap an wire and be called before or after
260
266
  */
261
- export type CorePikkuMiddleware<SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession> = (services: SingletonServices, wires: PikkuWire<unknown, unknown, false, UserSession>, next: () => Promise<void>) => Promise<void>;
267
+ export type CorePikkuMiddleware<SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession> = (services: SingletonServices, wires: PikkuWire, next: () => Promise<void>) => Promise<void>;
262
268
  /**
263
269
  * Priority levels for middleware execution order.
264
270
  * Lower priority runs first (outermost in the onion model).
@@ -356,11 +362,18 @@ export type CreateSingletonServices<Config extends CoreConfig, SingletonServices
356
362
  /**
357
363
  * Defines a function type for creating session-specific services.
358
364
  */
359
- export type CreateWireServices<SingletonServices extends CoreSingletonServices = CoreSingletonServices, Services extends CoreServices<SingletonServices> = CoreServices<SingletonServices>, UserSession extends CoreUserSession = CoreUserSession> = (services: SingletonServices, wire: PikkuWire<unknown, unknown, false, UserSession>) => Promise<WireServices<Services, SingletonServices>>;
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>>;
360
366
  /**
361
367
  * Defines a function type for creating config.
362
368
  */
363
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
+ };
364
377
  /**
365
378
  * Represents the documentation for a route, including summary, description, tags, and errors.
366
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
@@ -105,3 +105,5 @@ export const stopSingletonServices = async () => {
105
105
  await stopService(logger, name, service);
106
106
  }
107
107
  };
108
+ /** Wrap server lifecycle hooks so the inspector can discover them. */
109
+ export const pikkuServerLifecycle = (lifecycle) => lifecycle;
@@ -1,4 +1,4 @@
1
- import type { CoreSingletonServices, MiddlewareMetadata, PikkuWire } from '../../types/core.types.js';
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: PikkuWire, services: CoreSingletonServices, middlewares: readonly CorePikkuChannelMiddleware[]): PikkuWire;
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
- channel.send(data, isBinary);
54
- }
55
- else {
56
- // TODO: Websocket is closed, remove the channel from the topic
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, PikkuWire } from '../../types/core.types.js';
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: PikkuWire, options: {
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: PikkuWire): Promise<Out>;
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: PikkuWire, requiresAuthOrOptions?: boolean | {
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);
@@ -2,4 +2,4 @@
2
2
  * DSL (Domain Specific Language) workflow exports
3
3
  */
4
4
  export { addWorkflow } from './workflow-runner.js';
5
- export type { WorkflowStepOptions, WorkflowWireDoRPC, WorkflowWireDoInline, WorkflowWireSleep, WorkflowWireSuspend, InputSource, OutputBinding, RpcStepMeta, SimpleCondition, Condition, BranchStepMeta, ParallelGroupStepMeta, FanoutStepMeta, ReturnStepMeta, InlineStepMeta, SleepStepMeta, CancelStepMeta, SwitchCaseMeta, SwitchStepMeta, FilterStepMeta, ArrayPredicateStepMeta, WorkflowStepMeta, WorkflowStepWire, PikkuWorkflowWire, } from './workflow-dsl.types.js';
5
+ export type { WorkflowStepOptions, WorkflowWireDoRPC, WorkflowWireDoInline, WorkflowWireSleep, WorkflowWireSuspend, InputSource, OutputBinding, RpcStepMeta, SimpleCondition, Condition, BranchStepMeta, ParallelGroupStepMeta, FanoutStepMeta, ReturnStepMeta, InlineStepMeta, SleepStepMeta, CancelStepMeta, SuspendStepMeta, SwitchCaseMeta, SwitchStepMeta, FilterStepMeta, ArrayPredicateStepMeta, WorkflowStepMeta, WorkflowStepWire, PikkuWorkflowWire, } from './workflow-dsl.types.js';
@@ -230,6 +230,14 @@ export interface SwitchStepMeta {
230
230
  /** Default case steps (optional) */
231
231
  defaultSteps?: WorkflowStepMeta[];
232
232
  }
233
+ /**
234
+ * Suspend step metadata (workflow.suspend())
235
+ */
236
+ export interface SuspendStepMeta {
237
+ type: 'suspend';
238
+ /** Reason string passed to workflow.suspend() — becomes the durable step key */
239
+ reason: string;
240
+ }
233
241
  /**
234
242
  * Filter step metadata (array.filter)
235
243
  */
@@ -263,7 +271,7 @@ export interface ArrayPredicateStepMeta {
263
271
  /**
264
272
  * Workflow step metadata (extracted by inspector)
265
273
  */
266
- export type WorkflowStepMeta = RpcStepMeta | BranchStepMeta | ParallelGroupStepMeta | FanoutStepMeta | ReturnStepMeta | InlineStepMeta | SleepStepMeta | CancelStepMeta | SwitchStepMeta | FilterStepMeta | ArrayPredicateStepMeta | SetStepMeta;
274
+ export type WorkflowStepMeta = RpcStepMeta | BranchStepMeta | ParallelGroupStepMeta | FanoutStepMeta | ReturnStepMeta | InlineStepMeta | SleepStepMeta | CancelStepMeta | SuspendStepMeta | SwitchStepMeta | FilterStepMeta | ArrayPredicateStepMeta | SetStepMeta;
267
275
  /**
268
276
  * Workflow step wire context for RPC functions
269
277
  * Provides step-level metadata including retry attempt tracking
@@ -12,4 +12,4 @@ export { validateWorkflowWiring, computeEntryNodeIds, } from './graph/graph-vali
12
12
  export { pikkuWorkflowWorkerFunc, pikkuWorkflowOrchestratorFunc, pikkuWorkflowSleeperFunc, } from './workflow-queue-workers.js';
13
13
  export type { WorkflowStepInput as WorkflowStepQueueInput, PikkuWorkflowOrchestratorInput, PikkuWorkflowSleeperInput, } from './workflow-queue-workers.js';
14
14
  export type { WorkflowService, WorkflowServiceConfig, WorkflowPlannedStep, WorkflowRunWire, WorkflowStatus, WorkflowVersionStatus, StepStatus, WorkflowRun, WorkflowRunStatus, StepState, WorkflowRunService, WorkflowRunMirror, CoreWorkflow, PikkuWorkflow, ContextVariable, WorkflowContext, WorkflowsMeta, WorkflowRuntimeMeta, WorkflowsRuntimeMeta, WorkflowStepInput, WorkflowOrchestratorInput, WorkflowSleeperInput, } from './workflow.types.js';
15
- 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 './workflow.types.js';
15
+ export type { WorkflowStepOptions, WorkflowWireDoRPC, WorkflowWireDoInline, WorkflowWireSleep, WorkflowWireSuspend, InputSource, OutputBinding, RpcStepMeta, SimpleCondition, Condition, BranchCase, BranchStepMeta, ParallelGroupStepMeta, FanoutStepMeta, ReturnStepMeta, InlineStepMeta, SleepStepMeta, CancelStepMeta, SuspendStepMeta, SetStepMeta, SwitchCaseMeta, SwitchStepMeta, FilterStepMeta, ArrayPredicateStepMeta, WorkflowStepMeta, WorkflowStepWire, PikkuWorkflowWire, } from './workflow.types.js';
@@ -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
- protected wireQueueWorkers(): void;
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');
@@ -683,7 +690,7 @@ export class PikkuWorkflowService {
683
690
  throw new WorkflowRunFailedError(run.error?.message);
684
691
  }
685
692
  if (run.status === 'cancelled') {
686
- throw new Error('Workflow was cancelled');
693
+ throw new WorkflowRunCancelledError();
687
694
  }
688
695
  return run.output;
689
696
  }
@@ -1,7 +1,7 @@
1
1
  import type { SerializedError, CommonWireMeta } from '../../types/core.types.js';
2
2
  import type { CorePikkuFunctionConfig } from '../../function/functions.types.js';
3
3
  export type { WorkflowService } from '../../services/workflow-service.js';
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';
4
+ export type { WorkflowStepOptions, WorkflowWireDoRPC, WorkflowWireDoInline, WorkflowWireSleep, WorkflowWireSuspend, InputSource, OutputBinding, RpcStepMeta, SimpleCondition, Condition, BranchCase, BranchStepMeta, ParallelGroupStepMeta, FanoutStepMeta, ReturnStepMeta, InlineStepMeta, SleepStepMeta, CancelStepMeta, SuspendStepMeta, 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';
6
6
  export interface WorkflowRunWire {
7
7
  type: string;
@@ -19,8 +19,10 @@ export interface WorkflowServiceConfig {
19
19
  sleeperRPCName: string;
20
20
  }
21
21
  export interface WorkflowPlannedStep {
22
- /** Human-readable step label for UI timeline */
22
+ /** Durable step key matches the runtime step name stored in the DB */
23
23
  stepName: string;
24
+ /** Optional human-readable label for the UI timeline (falls back to stepName) */
25
+ displayName?: string;
24
26
  }
25
27
  /**
26
28
  * Workflow run status
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/core",
3
- "version": "0.12.40",
3
+ "version": "0.12.42",
4
4
  "author": "yasser.fadl@gmail.com",
5
5
  "license": "MIT",
6
6
  "module": "dist/index.js",
@@ -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: PikkuWire,
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: PikkuWire
238
+ wire: PikkuRawWire
238
239
  sessionService?: SessionService<CoreUserSession>
239
240
  credentialWireService?: PikkuCredentialWireService
240
241
  packageName?: string | null
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 { isSerializable, stopSingletonServices } from './utils.js'
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 { PikkuWire } from '../types/core.types.js'
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?: PikkuWire
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('rewireQueueWorkers registers workflow queues added after construction', () => {
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.rewireQueueWorkers()
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(runId: string): Promise<
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 { PikkuWire } from '../types/core.types.js'
1
+ import type { PikkuRawWire } from '../types/core.types.js'
2
2
 
3
- export type PikkuUserIdResolver = (wire: PikkuWire) => string | undefined
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)
@@ -18,6 +18,7 @@ import type {
18
18
  * Handles workflow execution, replay, orchestration logic, and run-level state
19
19
  */
20
20
  export interface WorkflowService {
21
+ wireQueueWorkers?(): void
21
22
  // Run-level state operations
22
23
  createRun(
23
24
  workflowName: string,