@pikku/core 0.11.1 → 0.11.2

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 (143) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/errors/error-handler.js +3 -3
  3. package/dist/function/function-runner.d.ts +4 -3
  4. package/dist/function/function-runner.js +60 -14
  5. package/dist/function/functions.types.d.ts +84 -5
  6. package/dist/function/functions.types.js +41 -1
  7. package/dist/index.d.ts +6 -2
  8. package/dist/index.js +5 -2
  9. package/dist/middleware/auth-apikey.d.ts +1 -1
  10. package/dist/middleware/auth-bearer.d.ts +1 -1
  11. package/dist/middleware/auth-cookie.d.ts +1 -1
  12. package/dist/middleware/timeout.d.ts +1 -1
  13. package/dist/middleware-runner.d.ts +3 -14
  14. package/dist/middleware-runner.js +8 -8
  15. package/dist/permissions.d.ts +3 -86
  16. package/dist/permissions.js +11 -67
  17. package/dist/pikku-state.d.ts +32 -104
  18. package/dist/pikku-state.js +140 -66
  19. package/dist/schema.d.ts +8 -6
  20. package/dist/schema.js +25 -13
  21. package/dist/services/workflow-service.d.ts +38 -0
  22. package/dist/services/workflow-service.js +1 -0
  23. package/dist/types/core.types.d.ts +12 -4
  24. package/dist/types/core.types.js +1 -1
  25. package/dist/types/state.types.d.ts +135 -0
  26. package/dist/types/state.types.js +1 -0
  27. package/dist/utils.d.ts +8 -0
  28. package/dist/utils.js +45 -0
  29. package/dist/wirings/channel/channel-handler.js +1 -1
  30. package/dist/wirings/channel/channel-runner.js +5 -5
  31. package/dist/wirings/channel/log-channels.js +1 -1
  32. package/dist/wirings/channel/serverless/serverless-channel-runner.js +2 -2
  33. package/dist/wirings/cli/channel/cli-channel-runner.js +3 -2
  34. package/dist/wirings/cli/cli-runner.js +10 -10
  35. package/dist/wirings/cli/cli.types.d.ts +1 -0
  36. package/dist/wirings/forge-node/forge-node.types.d.ts +120 -0
  37. package/dist/wirings/forge-node/forge-node.types.js +38 -0
  38. package/dist/wirings/forge-node/index.d.ts +1 -0
  39. package/dist/wirings/forge-node/index.js +1 -0
  40. package/dist/wirings/http/http-runner.d.ts +2 -2
  41. package/dist/wirings/http/http-runner.js +21 -12
  42. package/dist/wirings/http/http.types.d.ts +14 -1
  43. package/dist/wirings/http/log-http-routes.js +1 -1
  44. package/dist/wirings/http/routers/path-to-regex.js +2 -2
  45. package/dist/wirings/mcp/mcp-runner.js +21 -21
  46. package/dist/wirings/queue/queue-runner.js +6 -6
  47. package/dist/wirings/rpc/index.d.ts +1 -1
  48. package/dist/wirings/rpc/index.js +1 -1
  49. package/dist/wirings/rpc/rpc-runner.d.ts +7 -8
  50. package/dist/wirings/rpc/rpc-runner.js +62 -14
  51. package/dist/wirings/rpc/rpc-types.d.ts +7 -0
  52. package/dist/wirings/scheduler/log-schedulers.js +1 -1
  53. package/dist/wirings/scheduler/scheduler-runner.js +5 -5
  54. package/dist/wirings/trigger/index.d.ts +2 -0
  55. package/dist/wirings/trigger/index.js +2 -0
  56. package/dist/wirings/trigger/trigger-runner.d.ts +29 -0
  57. package/dist/wirings/trigger/trigger-runner.js +57 -0
  58. package/dist/wirings/trigger/trigger.types.d.ts +42 -0
  59. package/dist/wirings/trigger/trigger.types.js +1 -0
  60. package/dist/wirings/workflow/dsl/index.d.ts +5 -0
  61. package/dist/wirings/workflow/dsl/index.js +4 -0
  62. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +286 -0
  63. package/dist/wirings/workflow/dsl/workflow-dsl.types.js +5 -0
  64. package/dist/wirings/workflow/dsl/workflow-runner.d.ts +5 -0
  65. package/dist/wirings/workflow/dsl/workflow-runner.js +22 -0
  66. package/dist/wirings/workflow/graph/graph-node.d.ts +122 -0
  67. package/dist/wirings/workflow/graph/graph-node.js +58 -0
  68. package/dist/wirings/workflow/graph/graph-runner.d.ts +35 -0
  69. package/dist/wirings/workflow/graph/graph-runner.js +452 -0
  70. package/dist/wirings/workflow/graph/index.d.ts +3 -0
  71. package/dist/wirings/workflow/graph/index.js +3 -0
  72. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +95 -0
  73. package/dist/wirings/workflow/graph/workflow-graph.types.js +15 -0
  74. package/dist/wirings/workflow/index.d.ts +7 -12
  75. package/dist/wirings/workflow/index.js +6 -11
  76. package/dist/wirings/workflow/pikku-workflow-service.d.ts +96 -6
  77. package/dist/wirings/workflow/pikku-workflow-service.js +230 -103
  78. package/dist/wirings/workflow/wire-workflow.d.ts +42 -0
  79. package/dist/wirings/workflow/wire-workflow.js +53 -0
  80. package/dist/wirings/workflow/workflow-utils.d.ts +23 -0
  81. package/dist/wirings/workflow/workflow-utils.js +66 -0
  82. package/dist/wirings/workflow/workflow.types.d.ts +133 -211
  83. package/package.json +10 -1
  84. package/src/errors/error-handler.ts +3 -3
  85. package/src/function/function-runner.test.ts +1 -1
  86. package/src/function/function-runner.ts +86 -16
  87. package/src/function/functions.types.ts +126 -4
  88. package/src/index.ts +10 -5
  89. package/src/middleware-runner.ts +16 -9
  90. package/src/permissions.test.ts +28 -86
  91. package/src/permissions.ts +21 -80
  92. package/src/pikku-state.ts +156 -201
  93. package/src/schema.ts +42 -13
  94. package/src/services/workflow-service.ts +74 -0
  95. package/src/types/core.types.ts +12 -3
  96. package/src/types/state.types.ts +195 -0
  97. package/src/utils.ts +55 -0
  98. package/src/wirings/channel/channel-handler.ts +1 -1
  99. package/src/wirings/channel/channel-runner.ts +5 -5
  100. package/src/wirings/channel/local/local-channel-runner.test.ts +3 -1
  101. package/src/wirings/channel/log-channels.ts +1 -1
  102. package/src/wirings/channel/serverless/serverless-channel-runner.ts +2 -2
  103. package/src/wirings/cli/channel/cli-channel-runner.ts +4 -3
  104. package/src/wirings/cli/cli-runner.test.ts +19 -19
  105. package/src/wirings/cli/cli-runner.ts +10 -9
  106. package/src/wirings/cli/cli.types.ts +1 -0
  107. package/src/wirings/forge-node/forge-node.types.ts +135 -0
  108. package/src/wirings/forge-node/index.ts +1 -0
  109. package/src/wirings/http/http-runner.test.ts +2 -2
  110. package/src/wirings/http/http-runner.ts +30 -13
  111. package/src/wirings/http/http.types.ts +14 -0
  112. package/src/wirings/http/log-http-routes.ts +1 -1
  113. package/src/wirings/http/routers/path-to-regex.test.ts +30 -19
  114. package/src/wirings/http/routers/path-to-regex.ts +2 -2
  115. package/src/wirings/mcp/mcp-runner.ts +21 -21
  116. package/src/wirings/queue/queue-runner.test.ts +19 -19
  117. package/src/wirings/queue/queue-runner.ts +6 -6
  118. package/src/wirings/rpc/index.ts +1 -1
  119. package/src/wirings/rpc/rpc-runner.ts +88 -22
  120. package/src/wirings/rpc/rpc-types.ts +8 -0
  121. package/src/wirings/scheduler/log-schedulers.ts +1 -1
  122. package/src/wirings/scheduler/scheduler-runner.test.ts +27 -27
  123. package/src/wirings/scheduler/scheduler-runner.ts +5 -5
  124. package/src/wirings/trigger/index.ts +2 -0
  125. package/src/wirings/trigger/trigger-runner.ts +96 -0
  126. package/src/wirings/trigger/trigger.types.ts +56 -0
  127. package/src/wirings/workflow/dsl/index.ts +30 -0
  128. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +312 -0
  129. package/src/wirings/workflow/dsl/workflow-runner.ts +27 -0
  130. package/src/wirings/workflow/graph/graph-node.ts +227 -0
  131. package/src/wirings/workflow/graph/graph-runner.ts +694 -0
  132. package/src/wirings/workflow/graph/index.ts +3 -0
  133. package/src/wirings/workflow/graph/workflow-graph.types.ts +126 -0
  134. package/src/wirings/workflow/index.ts +53 -26
  135. package/src/wirings/workflow/pikku-workflow-service.ts +333 -117
  136. package/src/wirings/workflow/wire-workflow.ts +94 -0
  137. package/src/wirings/workflow/workflow-utils.ts +120 -0
  138. package/src/wirings/workflow/workflow.types.ts +166 -277
  139. package/tsconfig.tsbuildinfo +1 -1
  140. package/dist/wirings/workflow/workflow-runner.d.ts +0 -34
  141. package/dist/wirings/workflow/workflow-runner.js +0 -65
  142. package/src/function/function-runner.ts.bak +0 -188
  143. package/src/wirings/workflow/workflow-runner.ts +0 -72
@@ -1,5 +1,6 @@
1
1
  import type { Logger, LogLevel } from '../services/logger.js';
2
2
  import { VariablesService } from '../services/variables-service.js';
3
+ import { SecretService } from '../services/secret-service.js';
3
4
  import { SchemaService } from '../services/schema-service.js';
4
5
  import { JWTService } from '../services/jwt-service.js';
5
6
  import { PikkuHTTP } from '../wirings/http/http.types.js';
@@ -11,6 +12,8 @@ import { PikkuScheduledTask } from '../wirings/scheduler/scheduler.types.js';
11
12
  import { PikkuQueue, QueueService } from '../wirings/queue/queue.types.js';
12
13
  import { PikkuCLI } from '../wirings/cli/cli.types.js';
13
14
  import { PikkuWorkflowWire, WorkflowService, WorkflowServiceConfig, WorkflowStepWire } from '../wirings/workflow/workflow.types.js';
15
+ import type { PikkuGraphWire } from '../wirings/workflow/graph/workflow-graph.types.js';
16
+ import { PikkuTrigger } from '../wirings/trigger/trigger.types.js';
14
17
  import { SchedulerService } from '../services/scheduler-service.js';
15
18
  export type PikkuWiringTypes = 'http' | 'scheduler' | 'channel' | 'rpc' | 'queue' | 'mcp' | 'cli' | 'workflow';
16
19
  export interface FunctionServicesMeta {
@@ -124,6 +127,8 @@ export interface CoreSingletonServices<Config extends CoreConfig = CoreConfig> {
124
127
  logger: Logger;
125
128
  /** The variable service to be used */
126
129
  variables: VariablesService;
130
+ /** The secrets service to retrieve secrets */
131
+ secrets: SecretService;
127
132
  /** The workflow orchestrator service */
128
133
  workflowService?: WorkflowService;
129
134
  /** The queue service */
@@ -134,7 +139,7 @@ export interface CoreSingletonServices<Config extends CoreConfig = CoreConfig> {
134
139
  /**
135
140
  * Represents different forms of wire within Pikku and the outside world.
136
141
  */
137
- 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> = Partial<{
142
+ 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<{
138
143
  http: PikkuHTTP<In>;
139
144
  mcp: PikkuMCP<MCPTools>;
140
145
  rpc: TypedRPC;
@@ -144,6 +149,8 @@ export type PikkuWire<In = unknown, Out = unknown, HasInitialSession extends boo
144
149
  cli: PikkuCLI;
145
150
  workflow: TypedWorkflow;
146
151
  workflowStep: WorkflowStepWire;
152
+ graph: PikkuGraphWire;
153
+ trigger: PikkuTrigger<TriggerOutput>;
147
154
  initialSession: HasInitialSession extends true ? UserSession : UserSession | undefined;
148
155
  session: SessionService<UserSession>;
149
156
  }>;
@@ -160,8 +167,8 @@ export type CorePikkuMiddleware<SingletonServices extends CoreSingletonServices
160
167
  export type CorePikkuMiddlewareConfig<SingletonServices extends CoreSingletonServices = CoreSingletonServices, UserSession extends CoreUserSession = CoreUserSession> = {
161
168
  /** The middleware function */
162
169
  func: CorePikkuMiddleware<SingletonServices, UserSession>;
163
- /** Optional human-readable name for the middleware */
164
- name?: string;
170
+ /** Optional human-readable title for the middleware */
171
+ title?: string;
165
172
  /** Optional description of what the middleware does */
166
173
  description?: string;
167
174
  };
@@ -191,7 +198,7 @@ export type CorePikkuMiddlewareGroup<SingletonServices extends CoreSingletonServ
191
198
  *
192
199
  * // Configuration object syntax with metadata
193
200
  * export const logMiddleware = pikkuMiddleware({
194
- * name: 'Request Logger',
201
+ * title: 'Request Logger',
195
202
  * description: 'Logs request information',
196
203
  * func: async ({ logger }, next) => {
197
204
  * logger.info('Request started')
@@ -241,6 +248,7 @@ export type CreateConfig<Config extends CoreConfig, RemainingArgs extends any[]
241
248
  */
242
249
  export type CommonWireMeta = {
243
250
  pikkuFuncName: string;
251
+ title?: string;
244
252
  tags?: string[];
245
253
  summary?: string;
246
254
  description?: string;
@@ -14,7 +14,7 @@
14
14
  *
15
15
  * // Configuration object syntax with metadata
16
16
  * export const logMiddleware = pikkuMiddleware({
17
- * name: 'Request Logger',
17
+ * title: 'Request Logger',
18
18
  * description: 'Logs request information',
19
19
  * func: async ({ logger }, next) => {
20
20
  * logger.info('Request started')
@@ -0,0 +1,135 @@
1
+ import { PikkuError, ErrorDetails } from '../errors/error-handler.js';
2
+ import { CorePikkuFunctionConfig, CorePermissionGroup, CorePikkuPermission } from '../function/functions.types.js';
3
+ import { CoreChannel, ChannelsMeta } from '../wirings/channel/channel.types.js';
4
+ import { CLIMeta, CLIProgramState } from '../wirings/cli/cli.types.js';
5
+ import { HTTPMethod, CoreHTTPFunctionWiring, HTTPWiringsMeta } from '../wirings/http/http.types.js';
6
+ import { CoreMCPResource, MCPResourceMeta, CoreMCPTool, MCPToolMeta, CoreMCPPrompt, MCPPromptMeta } from '../wirings/mcp/mcp.types.js';
7
+ import { CoreQueueWorker, QueueWorkersMeta } from '../wirings/queue/queue.types.js';
8
+ import { CoreScheduledTask, ScheduledTasksMeta } from '../wirings/scheduler/scheduler.types.js';
9
+ import { CoreWorkflow, WorkflowsRuntimeMeta, WorkflowWires } from '../wirings/workflow/workflow.types.js';
10
+ import type { WorkflowGraphDefinition, GraphNodeConfig } from '../wirings/workflow/graph/workflow-graph.types.js';
11
+ import { CoreTrigger, TriggerMeta } from '../wirings/trigger/trigger.types.js';
12
+ import { FunctionsMeta, CorePikkuMiddleware, CorePikkuMiddlewareGroup, FunctionServicesMeta, CreateConfig, CreateSingletonServices, CreateWireServices, CoreConfig, CoreSingletonServices, CoreServices, CoreUserSession } from './core.types.js';
13
+ /**
14
+ * State structure for an individual package
15
+ */
16
+ export interface PikkuPackageState {
17
+ function: {
18
+ meta: FunctionsMeta;
19
+ functions: Map<string, CorePikkuFunctionConfig<any, any>>;
20
+ };
21
+ rpc: {
22
+ meta: Record<string, string>;
23
+ files: Map<string, {
24
+ exportedName: string;
25
+ path: string;
26
+ }>;
27
+ /** Maps namespace aliases to package names (e.g., 'ext' -> '@pikku/templates-function-external') */
28
+ externalPackages: Map<string, string>;
29
+ };
30
+ http: {
31
+ middleware: Map<string, CorePikkuMiddleware<any, any>[]>;
32
+ permissions: Map<string, CorePermissionGroup | CorePikkuPermission[]>;
33
+ routes: Map<HTTPMethod, Map<string, CoreHTTPFunctionWiring<any, any, any>>>;
34
+ meta: HTTPWiringsMeta;
35
+ };
36
+ channel: {
37
+ channels: Map<string, CoreChannel<any, any>>;
38
+ meta: ChannelsMeta;
39
+ };
40
+ scheduler: {
41
+ tasks: Map<string, CoreScheduledTask>;
42
+ meta: ScheduledTasksMeta;
43
+ };
44
+ queue: {
45
+ registrations: Map<string, CoreQueueWorker>;
46
+ meta: QueueWorkersMeta;
47
+ };
48
+ workflows: {
49
+ registrations: Map<string, CoreWorkflow>;
50
+ graphRegistrations: Map<string, WorkflowGraphDefinition<any>>;
51
+ /** DSL workflow wirings (from wireWorkflow({ func: ... })) */
52
+ wirings: Map<any, {
53
+ wires: WorkflowWires;
54
+ func: any;
55
+ }>;
56
+ /** Graph workflow wirings (from wireWorkflow({ graph: ... })) */
57
+ graphWirings: Map<any, {
58
+ wires: WorkflowWires;
59
+ graph: Record<string, GraphNodeConfig<string>>;
60
+ }>;
61
+ meta: WorkflowsRuntimeMeta;
62
+ };
63
+ trigger: {
64
+ triggers: Map<string, CoreTrigger>;
65
+ meta: TriggerMeta;
66
+ };
67
+ mcp: {
68
+ resources: Map<string, CoreMCPResource>;
69
+ resourcesMeta: MCPResourceMeta;
70
+ tools: Map<string, CoreMCPTool>;
71
+ toolsMeta: MCPToolMeta;
72
+ prompts: Map<string, CoreMCPPrompt>;
73
+ promptsMeta: MCPPromptMeta;
74
+ };
75
+ cli: {
76
+ meta: CLIMeta | Record<string, any>;
77
+ programs: Record<string, CLIProgramState>;
78
+ };
79
+ middleware: {
80
+ tagGroup: Record<string, CorePikkuMiddlewareGroup>;
81
+ httpGroup: Record<string, CorePikkuMiddlewareGroup>;
82
+ tagGroupMeta: Record<string, {
83
+ exportName: string | null;
84
+ sourceFile: string;
85
+ position: number;
86
+ services: FunctionServicesMeta;
87
+ middlewareCount: number;
88
+ isFactory: boolean;
89
+ }>;
90
+ httpGroupMeta: Record<string, {
91
+ exportName: string | null;
92
+ sourceFile: string;
93
+ position: number;
94
+ services: FunctionServicesMeta;
95
+ middlewareCount: number;
96
+ isFactory: boolean;
97
+ }>;
98
+ };
99
+ permissions: {
100
+ tagGroup: Record<string, CorePermissionGroup | CorePikkuPermission[]>;
101
+ httpGroup: Record<string, CorePermissionGroup | CorePikkuPermission[]>;
102
+ tagGroupMeta: Record<string, {
103
+ exportName: string | null;
104
+ sourceFile: string;
105
+ position: number;
106
+ services: FunctionServicesMeta;
107
+ permissionCount: number;
108
+ isFactory: boolean;
109
+ }>;
110
+ httpGroupMeta: Record<string, {
111
+ exportName: string | null;
112
+ sourceFile: string;
113
+ position: number;
114
+ services: FunctionServicesMeta;
115
+ permissionCount: number;
116
+ isFactory: boolean;
117
+ }>;
118
+ };
119
+ misc: {
120
+ errors: Map<PikkuError, ErrorDetails>;
121
+ schemas: Map<string, any>;
122
+ middleware: Record<string, CorePikkuMiddleware[]>;
123
+ permissions: Record<string, CorePermissionGroup | CorePikkuPermission[]>;
124
+ };
125
+ package: {
126
+ /** Service factory functions for external packages */
127
+ factories: {
128
+ createConfig?: CreateConfig<CoreConfig>;
129
+ createSingletonServices?: CreateSingletonServices<CoreConfig, CoreSingletonServices>;
130
+ createWireServices?: CreateWireServices<CoreSingletonServices, CoreServices, CoreUserSession>;
131
+ } | null;
132
+ /** Cached singleton services for this package */
133
+ singletonServices: CoreSingletonServices | null;
134
+ };
135
+ }
@@ -0,0 +1 @@
1
+ export {};
package/dist/utils.d.ts CHANGED
@@ -1,5 +1,13 @@
1
1
  import { Logger } from './services/logger.js';
2
+ import { CoreSingletonServices } from './types/core.types.js';
2
3
  export declare const closeWireServices: (logger: Logger, wireServices: Record<string, any>) => Promise<void>;
3
4
  export declare const createWeakUID: () => string;
4
5
  export declare const isSerializable: (data: any) => boolean;
5
6
  export declare const freezeDedupe: <T>(arr?: readonly T[] | T[] | undefined) => readonly T[];
7
+ /**
8
+ * Stop all singleton services, including external package services.
9
+ * External package services are stopped first, then the parent services.
10
+ *
11
+ * @param singletonServices - The parent singleton services to stop
12
+ */
13
+ export declare const stopSingletonServices: (singletonServices: CoreSingletonServices) => Promise<void>;
package/dist/utils.js CHANGED
@@ -45,3 +45,48 @@ export const freezeDedupe = (arr) => {
45
45
  }
46
46
  return Object.freeze(out);
47
47
  };
48
+ /**
49
+ * Stop a single service by calling its stop method if it exists
50
+ */
51
+ const stopService = async (logger, name, service) => {
52
+ const stop = service?.stop;
53
+ if (stop) {
54
+ logger.info(`Stopping singleton service: ${name}`);
55
+ try {
56
+ await stop.call(service);
57
+ }
58
+ catch (e) {
59
+ logger.error(`Error stopping service ${name}:`, e);
60
+ }
61
+ }
62
+ };
63
+ /**
64
+ * Stop all singleton services, including external package services.
65
+ * External package services are stopped first, then the parent services.
66
+ *
67
+ * @param singletonServices - The parent singleton services to stop
68
+ */
69
+ export const stopSingletonServices = async (singletonServices) => {
70
+ const logger = singletonServices.logger;
71
+ // First, stop all external package singleton services
72
+ if (globalThis.pikkuState) {
73
+ for (const [packageName, packageState] of globalThis.pikkuState) {
74
+ // Skip main package - we handle it separately
75
+ if (packageName === '__main__')
76
+ continue;
77
+ const packageServices = packageState.package?.singletonServices;
78
+ if (packageServices) {
79
+ logger.info(`Stopping singleton services for package: ${packageName}`);
80
+ for (const [name, service] of Object.entries(packageServices)) {
81
+ await stopService(logger, `${packageName}/${name}`, service);
82
+ }
83
+ // Clear the cached services
84
+ packageState.package.singletonServices = null;
85
+ }
86
+ }
87
+ }
88
+ // Then stop the parent singleton services
89
+ for (const [name, service] of Object.entries(singletonServices)) {
90
+ await stopService(logger, name, service);
91
+ }
92
+ };
@@ -1,7 +1,7 @@
1
1
  import { pikkuState } from '../../pikku-state.js';
2
2
  import { runPikkuFunc } from '../../function/function-runner.js';
3
3
  const getRouteMeta = (channelName, routingProperty, routerValue) => {
4
- const channelMeta = pikkuState('channel', 'meta')[channelName];
4
+ const channelMeta = pikkuState(null, 'channel', 'meta')[channelName];
5
5
  if (!channelMeta) {
6
6
  throw new Error(`Channel ${channelName} not found`);
7
7
  }
@@ -9,12 +9,12 @@ import { httpRouter } from '../http/routers/http-router.js';
9
9
  */
10
10
  export const wireChannel = (channel) => {
11
11
  // Get the channel metadata
12
- const channelsMeta = pikkuState('channel', 'meta');
12
+ const channelsMeta = pikkuState(null, 'channel', 'meta');
13
13
  const channelMeta = channelsMeta[channel.name];
14
14
  if (!channelMeta) {
15
15
  throw new Error(`Channel metadata not found for channel: ${channel.name}`);
16
16
  }
17
- pikkuState('channel', 'channels').set(channel.name, channel);
17
+ pikkuState(null, 'channel', 'channels').set(channel.name, channel);
18
18
  // Register onConnect function if provided
19
19
  if (channel.onConnect && channelMeta.connect) {
20
20
  addFunction(channelMeta.connect.pikkuFuncName, channel.onConnect);
@@ -50,19 +50,19 @@ export const wireChannel = (channel) => {
50
50
  });
51
51
  }
52
52
  // Store the channel configuration
53
- pikkuState('channel', 'channels').set(channel.name, channel);
53
+ pikkuState(null, 'channel', 'channels').set(channel.name, channel);
54
54
  };
55
55
  const getMatchingChannelConfig = (path) => {
56
56
  const matchedPath = httpRouter.match('get', path);
57
57
  if (!matchedPath) {
58
58
  return null;
59
59
  }
60
- const meta = pikkuState('channel', 'meta');
60
+ const meta = pikkuState(null, 'channel', 'meta');
61
61
  const channelMeta = Object.values(meta).find((channelConfig) => channelConfig.route === matchedPath.route);
62
62
  if (!channelMeta) {
63
63
  return null;
64
64
  }
65
- const channels = pikkuState('channel', 'channels');
65
+ const channels = pikkuState(null, 'channel', 'channels');
66
66
  const channelConfig = channels.get(channelMeta.name);
67
67
  if (!channelConfig) {
68
68
  return null;
@@ -4,7 +4,7 @@ import { pikkuState } from '../../pikku-state.js';
4
4
  * @param logger - A logger for logging information.
5
5
  */
6
6
  export const logChannels = (logger) => {
7
- const channels = pikkuState('channel', 'channels');
7
+ const channels = pikkuState(null, 'channel', 'channels');
8
8
  if (channels.size === 0) {
9
9
  logger.info('No channels added');
10
10
  return;
@@ -8,9 +8,9 @@ import { pikkuState } from '../../../pikku-state.js';
8
8
  import { PikkuFetchHTTPRequest } from '../../http/pikku-fetch-http-request.js';
9
9
  import { runChannelLifecycleWithMiddleware } from '../channel-common.js';
10
10
  const getVariablesForChannel = ({ channelId, channelName, channelHandlerFactory, openingData, }) => {
11
- const channels = pikkuState('channel', 'channels');
11
+ const channels = pikkuState(null, 'channel', 'channels');
12
12
  const channelConfig = channels.get(channelName);
13
- const channelsMeta = pikkuState('channel', 'meta');
13
+ const channelsMeta = pikkuState(null, 'channel', 'meta');
14
14
  const meta = channelsMeta[channelName];
15
15
  if (!channelConfig) {
16
16
  throw new Error(`Channel not found: ${channelName}`);
@@ -12,10 +12,11 @@ const defaultJSONRenderer = (_services, data) => {
12
12
  */
13
13
  export async function executeCLIViaChannel({ programName, pikkuWS, args = process.argv.slice(2), renderers = {}, defaultRenderer, }) {
14
14
  // Get CLI metadata from state
15
- const allCLIMeta = pikkuState('cli', 'meta');
16
- if (!allCLIMeta) {
15
+ const cliMeta = pikkuState(null, 'cli', 'meta');
16
+ if (!cliMeta || !('programs' in cliMeta)) {
17
17
  throw new Error('[PKU342] CLI metadata not found. No CLI wirings were registered. See https://pikku.dev/docs/pikku-cli/errors/pku342 for more information.');
18
18
  }
19
+ const allCLIMeta = cliMeta;
19
20
  const programMeta = allCLIMeta.programs[programName];
20
21
  if (!programMeta) {
21
22
  console.error(`Error: CLI program "${programName}" not found`);
@@ -27,12 +27,12 @@ const defaultJSONRenderer = (_services, data) => {
27
27
  */
28
28
  export const wireCLI = (cli) => {
29
29
  // Get the existing metadata that was generated during inspection
30
- const cliMeta = pikkuState('cli', 'meta') || {};
30
+ const cliMeta = pikkuState(null, 'cli', 'meta') || {};
31
31
  if (!cliMeta.programs?.[cli.program]) {
32
32
  throw new Error(`CLI metadata not found for program '${cli.program}'. Did you run 'pikku all'?`);
33
33
  }
34
34
  // Get existing programs state and add this program
35
- const programs = pikkuState('cli', 'programs') || {};
35
+ const programs = pikkuState(null, 'cli', 'programs') || {};
36
36
  programs[cli.program] = {
37
37
  defaultRenderer: (cli.render ||
38
38
  defaultJSONRenderer),
@@ -40,7 +40,7 @@ export const wireCLI = (cli) => {
40
40
  renderers: {},
41
41
  tags: cli.tags,
42
42
  };
43
- pikkuState('cli', 'programs', programs);
43
+ pikkuState(null, 'cli', 'programs', programs);
44
44
  // Register all command functions recursively
45
45
  registerCLICommands(cli.commands, [], cli.options || {}, cli.program);
46
46
  };
@@ -72,7 +72,7 @@ function unwrapFunc(command) {
72
72
  */
73
73
  function registerCLICommands(commands, path = [], inheritedOptions = {}, program) {
74
74
  // Get the CLI metadata to find actual function names
75
- const cliMeta = pikkuState('cli', 'meta').programs[program];
75
+ const cliMeta = pikkuState(null, 'cli', 'meta').programs[program];
76
76
  for (const [name, command] of Object.entries(commands)) {
77
77
  const fullPath = [...path, name];
78
78
  const commandId = fullPath.join('.');
@@ -96,7 +96,7 @@ function registerCLICommands(commands, path = [], inheritedOptions = {}, program
96
96
  const commandOptions = typeof command === 'object' ? command.options || {} : {};
97
97
  const mergedOptions = { ...inheritedOptions, ...commandOptions };
98
98
  // Store the options and middleware in program state for use during execution
99
- const programs = pikkuState('cli', 'programs');
99
+ const programs = pikkuState(null, 'cli', 'programs');
100
100
  if (programs[program]) {
101
101
  if (!programs[program].commandOptions) {
102
102
  programs[program].commandOptions = {};
@@ -127,10 +127,10 @@ function registerCLICommands(commands, path = [], inheritedOptions = {}, program
127
127
  * Plucks only the data that the function expects based on its schema
128
128
  */
129
129
  function pluckCLIData(mergedData, funcName, availableOptions) {
130
- const funcMeta = pikkuState('function', 'meta')[funcName];
130
+ const funcMeta = pikkuState(null, 'function', 'meta')[funcName];
131
131
  const schemaName = funcMeta?.inputSchemaName;
132
132
  const schema = schemaName
133
- ? pikkuState('misc', 'schemas').get(schemaName)
133
+ ? pikkuState(null, 'misc', 'schemas').get(schemaName)
134
134
  : null;
135
135
  if (schema && schema.properties) {
136
136
  // If we have a schema, only include fields that are in the schema
@@ -156,7 +156,7 @@ function pluckCLIData(mergedData, funcName, availableOptions) {
156
156
  */
157
157
  export async function runCLICommand({ program, commandPath, data, singletonServices, createWireServices, }) {
158
158
  // Get the command metadata to find the function name
159
- const cliMeta = pikkuState('cli', 'meta');
159
+ const cliMeta = pikkuState(null, 'cli', 'meta');
160
160
  const programMeta = cliMeta.programs?.[program];
161
161
  if (!programMeta) {
162
162
  throw new NotFoundError(`Program not found: ${program}`);
@@ -175,7 +175,7 @@ export async function runCLICommand({ program, commandPath, data, singletonServi
175
175
  }
176
176
  const funcName = currentCommand.pikkuFuncName;
177
177
  // Get program-specific data
178
- const programs = pikkuState('cli', 'programs') || {};
178
+ const programs = pikkuState(null, 'cli', 'programs') || {};
179
179
  const programData = programs[program];
180
180
  // Combine program middleware + command middleware from the hierarchy
181
181
  const allWireMiddleware = [
@@ -266,7 +266,7 @@ export async function executeCLI({ programName, args, createConfig, createSingle
266
266
  }
267
267
  try {
268
268
  // Get CLI metadata from state
269
- const allCLIMeta = pikkuState('cli', 'meta');
269
+ const allCLIMeta = pikkuState(null, 'cli', 'meta');
270
270
  if (!allCLIMeta) {
271
271
  throw new Error('[PKU342] CLI metadata not found. No CLI wirings were registered. See https://pikku.dev/docs/pikku-cli/errors/pku342 for more information.');
272
272
  }
@@ -140,6 +140,7 @@ export type ExtractFunctionOutput<Func> = Func extends CorePikkuFunctionConfig<i
140
140
  export type CoreCLICommandConfig<FuncConfig, PikkuMiddleware extends CorePikkuMiddleware<any, any> = CorePikkuMiddleware<any, any>, PikkuCLIRender extends CorePikkuCLIRender<any, any, any> = CorePikkuCLIRender<any, any>, Params extends string = string> = {
141
141
  parameters?: ValidateParameters<Params, ExtractFunctionInput<FuncConfig>>;
142
142
  func?: FuncConfig;
143
+ title?: string;
143
144
  description?: string;
144
145
  render?: PikkuCLIRender;
145
146
  options?: {
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Forge node types for workflow builder visualization.
3
+ * wireForgeNode is metadata-only - no runtime behavior.
4
+ * The CLI extracts this via AST and generates JSON metadata.
5
+ */
6
+ /**
7
+ * The type of forge node.
8
+ * - trigger: Starts workflows (webhooks, schedules, events)
9
+ * - action: Operations that do something (API calls, send email, database ops)
10
+ * - end: Terminal nodes with no outputs (logging, notifications)
11
+ */
12
+ export type ForgeNodeType = 'trigger' | 'action' | 'end';
13
+ /**
14
+ * Configuration for wireForgeNode.
15
+ * This is the input type that developers use when defining nodes.
16
+ */
17
+ export type CoreForgeNode = {
18
+ /** Unique identifier for this node */
19
+ name: string;
20
+ /** Human-readable name for UI display */
21
+ displayName: string;
22
+ /** Grouping category (validated against forge.node.categories in config) */
23
+ category: string;
24
+ /** Node type determining behavior and outputs */
25
+ type: ForgeNodeType;
26
+ /** RPC name to call when node executes (local name, Forge handles package aliasing) */
27
+ rpc: string;
28
+ /** Optional description for UI */
29
+ description?: string;
30
+ /** Whether to add an error output port alongside the default output */
31
+ errorOutput?: boolean;
32
+ /** Optional tags for filtering/categorization */
33
+ tags?: string[];
34
+ };
35
+ /**
36
+ * Metadata generated for each forge node.
37
+ * This is the output type that the CLI generates.
38
+ */
39
+ export type ForgeNodeMeta = {
40
+ name: string;
41
+ displayName: string;
42
+ category: string;
43
+ type: ForgeNodeType;
44
+ /** RPC name (local, remapped by Forge for external packages) */
45
+ rpc: string;
46
+ description?: string;
47
+ /** Whether node has error output port */
48
+ errorOutput: boolean;
49
+ /** Input schema name extracted from RPC */
50
+ inputSchemaName: string | null;
51
+ /** Output schema name extracted from RPC */
52
+ outputSchemaName: string | null;
53
+ tags?: string[];
54
+ };
55
+ /**
56
+ * Record of all forge node metadata, keyed by node name.
57
+ */
58
+ export type ForgeNodesMeta = Record<string, ForgeNodeMeta>;
59
+ /**
60
+ * No-op function for wireForgeNode.
61
+ * This exists purely for TypeScript type checking and will be tree-shaken.
62
+ * The CLI extracts metadata via AST parsing.
63
+ */
64
+ export declare const wireForgeNode: (_config: CoreForgeNode) => void;
65
+ /**
66
+ * Configuration for wireForgeCredential.
67
+ * Declares credentials/secrets required by a Forge package.
68
+ * Uses Zod for schema definition - converted to JSON Schema at build time.
69
+ */
70
+ export type CoreForgeCredential<T = unknown> = {
71
+ /** Unique identifier for this credential */
72
+ name: string;
73
+ /** Human-readable name for UI display */
74
+ displayName: string;
75
+ /** Optional description for UI */
76
+ description?: string;
77
+ /** Key used with SecretService.getSecret() to retrieve the credential */
78
+ secretId: string;
79
+ /** Zod schema defining the structure of the credential */
80
+ schema: T;
81
+ };
82
+ /**
83
+ * Metadata generated for each forge credential.
84
+ * Schema is converted from Zod to JSON Schema at build time.
85
+ */
86
+ export type ForgeCredentialMeta = {
87
+ name: string;
88
+ displayName: string;
89
+ description?: string;
90
+ secretId: string;
91
+ /** JSON Schema (converted from Zod at build time) or zodLookup reference name */
92
+ schema: Record<string, unknown> | string;
93
+ };
94
+ /**
95
+ * Record of all forge credential metadata, keyed by credential name.
96
+ */
97
+ export type ForgeCredentialsMeta = Record<string, ForgeCredentialMeta>;
98
+ /**
99
+ * No-op function for wireForgeCredential.
100
+ * This exists purely for TypeScript type checking and will be tree-shaken.
101
+ * The CLI extracts metadata via AST parsing.
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * import { z } from 'zod'
106
+ *
107
+ * const slackCredentials = z.object({
108
+ * botToken: z.string().describe('Bot OAuth token'),
109
+ * signingSecret: z.string().describe('Request signing secret')
110
+ * })
111
+ *
112
+ * wireForgeCredential({
113
+ * name: 'slack',
114
+ * displayName: 'Slack Credentials',
115
+ * secretId: 'SLACK_CREDENTIALS',
116
+ * schema: slackCredentials
117
+ * })
118
+ * ```
119
+ */
120
+ export declare const wireForgeCredential: <T>(_config: CoreForgeCredential<T>) => void;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Forge node types for workflow builder visualization.
3
+ * wireForgeNode is metadata-only - no runtime behavior.
4
+ * The CLI extracts this via AST and generates JSON metadata.
5
+ */
6
+ /**
7
+ * No-op function for wireForgeNode.
8
+ * This exists purely for TypeScript type checking and will be tree-shaken.
9
+ * The CLI extracts metadata via AST parsing.
10
+ */
11
+ export const wireForgeNode = (_config) => {
12
+ // No-op - metadata only, extracted by CLI via AST
13
+ };
14
+ /**
15
+ * No-op function for wireForgeCredential.
16
+ * This exists purely for TypeScript type checking and will be tree-shaken.
17
+ * The CLI extracts metadata via AST parsing.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * import { z } from 'zod'
22
+ *
23
+ * const slackCredentials = z.object({
24
+ * botToken: z.string().describe('Bot OAuth token'),
25
+ * signingSecret: z.string().describe('Request signing secret')
26
+ * })
27
+ *
28
+ * wireForgeCredential({
29
+ * name: 'slack',
30
+ * displayName: 'Slack Credentials',
31
+ * secretId: 'SLACK_CREDENTIALS',
32
+ * schema: slackCredentials
33
+ * })
34
+ * ```
35
+ */
36
+ export const wireForgeCredential = (_config) => {
37
+ // No-op - metadata only, extracted by CLI via AST
38
+ };
@@ -0,0 +1 @@
1
+ export * from './forge-node.types.js';
@@ -0,0 +1 @@
1
+ export * from './forge-node.types.js';
@@ -31,7 +31,7 @@ import { PikkuFetchHTTPResponse } from './pikku-fetch-http-response.js';
31
31
  * ])
32
32
  * ```
33
33
  */
34
- export declare const addHTTPMiddleware: <PikkuMiddleware extends CorePikkuMiddleware>(pattern: string, middleware: CorePikkuMiddlewareGroup) => CorePikkuMiddlewareGroup;
34
+ export declare const addHTTPMiddleware: <PikkuMiddleware extends CorePikkuMiddleware>(pattern: string, middleware: CorePikkuMiddlewareGroup, packageName?: string | null) => CorePikkuMiddlewareGroup;
35
35
  /**
36
36
  * Registers HTTP permissions for a specific route pattern.
37
37
  *
@@ -61,7 +61,7 @@ export declare const addHTTPMiddleware: <PikkuMiddleware extends CorePikkuMiddle
61
61
  * ])
62
62
  * ```
63
63
  */
64
- export declare const addHTTPPermission: <PikkuPermission extends CorePikkuPermission>(pattern: string, permissions: CorePermissionGroup | CorePikkuPermission[]) => CorePermissionGroup | CorePikkuPermission[];
64
+ export declare const addHTTPPermission: <PikkuPermission extends CorePikkuPermission>(pattern: string, permissions: CorePermissionGroup | CorePikkuPermission[], packageName?: string | null) => CorePermissionGroup | CorePikkuPermission[];
65
65
  /**
66
66
  * Adds a new route to the global HTTP route registry.
67
67
  *