@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
@@ -56,13 +56,13 @@ export const wireMCPResource = <
56
56
  >(
57
57
  mcpResource: CoreMCPResource<PikkuFunctionConfig>
58
58
  ) => {
59
- const resourcesMeta = pikkuState('mcp', 'resourcesMeta')
59
+ const resourcesMeta = pikkuState(null, 'mcp', 'resourcesMeta')
60
60
  const mcpResourceMeta = resourcesMeta[mcpResource.uri]
61
61
  if (!mcpResourceMeta) {
62
62
  throw new Error(`MCP resource metadata not found for '${mcpResource.uri}'`)
63
63
  }
64
64
  addFunction(mcpResourceMeta.pikkuFuncName, mcpResource.func as any)
65
- const resources = pikkuState('mcp', 'resources')
65
+ const resources = pikkuState(null, 'mcp', 'resources')
66
66
  if (resources.has(mcpResource.uri)) {
67
67
  throw new Error(`MCP resource already exists: ${mcpResource.uri}`)
68
68
  }
@@ -76,13 +76,13 @@ export const wireMCPTool = <
76
76
  >(
77
77
  mcpTool: CoreMCPTool<PikkuFunctionConfig>
78
78
  ) => {
79
- const toolsMeta = pikkuState('mcp', 'toolsMeta')
79
+ const toolsMeta = pikkuState(null, 'mcp', 'toolsMeta')
80
80
  const mcpToolMeta = toolsMeta[mcpTool.name]
81
81
  if (!mcpToolMeta) {
82
82
  throw new Error(`MCP tool metadata not found for '${mcpTool.name}'`)
83
83
  }
84
84
  addFunction(mcpToolMeta.pikkuFuncName, mcpTool.func as any)
85
- const tools = pikkuState('mcp', 'tools')
85
+ const tools = pikkuState(null, 'mcp', 'tools')
86
86
  if (tools.has(mcpTool.name)) {
87
87
  throw new Error(`MCP tool already exists: ${mcpTool.name}`)
88
88
  }
@@ -96,13 +96,13 @@ export const wireMCPPrompt = <
96
96
  >(
97
97
  mcpPrompt: CoreMCPPrompt<PikkuFunctionConfig>
98
98
  ) => {
99
- const promptsMeta = pikkuState('mcp', 'promptsMeta')
99
+ const promptsMeta = pikkuState(null, 'mcp', 'promptsMeta')
100
100
  const mcpPromptMeta = promptsMeta[mcpPrompt.name]
101
101
  if (!mcpPromptMeta) {
102
102
  throw new Error(`MCP prompt metadata not found for '${mcpPrompt.name}'`)
103
103
  }
104
104
  addFunction(mcpPromptMeta.pikkuFuncName, mcpPrompt.func as any)
105
- const prompts = pikkuState('mcp', 'prompts')
105
+ const prompts = pikkuState(null, 'mcp', 'prompts')
106
106
  if (prompts.has(mcpPrompt.name)) {
107
107
  throw new Error(`MCP prompt already exists: ${mcpPrompt.name}`)
108
108
  }
@@ -118,8 +118,8 @@ export async function runMCPResource(
118
118
  let pikkuFuncName: string | undefined
119
119
  let extractedParams: Record<string, string> = {}
120
120
 
121
- const metas = pikkuState('mcp', 'resourcesMeta')
122
- const endpoints = pikkuState('mcp', 'resources')
121
+ const metas = pikkuState(null, 'mcp', 'resourcesMeta')
122
+ const endpoints = pikkuState(null, 'mcp', 'resources')
123
123
 
124
124
  if (endpoints.has(uri)) {
125
125
  endpoint = endpoints.get(uri)
@@ -170,8 +170,8 @@ export async function runMCPTool(
170
170
  params: RunMCPEndpointParams,
171
171
  name: string
172
172
  ) {
173
- const endpoint = pikkuState('mcp', 'tools').get(name)
174
- const meta = pikkuState('mcp', 'toolsMeta')[name]
173
+ const endpoint = pikkuState(null, 'mcp', 'tools').get(name)
174
+ const meta = pikkuState(null, 'mcp', 'toolsMeta')[name]
175
175
  return await runMCPPikkuFunc(
176
176
  request,
177
177
  'tool',
@@ -187,8 +187,8 @@ export async function runMCPPrompt(
187
187
  params: RunMCPEndpointParams,
188
188
  name: string
189
189
  ) {
190
- const endpoint = pikkuState('mcp', 'prompts').get(name)
191
- const meta = pikkuState('mcp', 'promptsMeta')[name]
190
+ const endpoint = pikkuState(null, 'mcp', 'prompts').get(name)
191
+ const meta = pikkuState(null, 'mcp', 'promptsMeta')[name]
192
192
  return await runMCPPikkuFunc(
193
193
  request,
194
194
  'prompt',
@@ -242,11 +242,11 @@ async function runMCPPikkuFunc(
242
242
  // Get metadata for the MCP endpoint to access pre-resolved middleware
243
243
  let meta: any
244
244
  if (type === 'resource') {
245
- meta = pikkuState('mcp', 'resourcesMeta')[name]
245
+ meta = pikkuState(null, 'mcp', 'resourcesMeta')[name]
246
246
  } else if (type === 'tool') {
247
- meta = pikkuState('mcp', 'toolsMeta')[name]
247
+ meta = pikkuState(null, 'mcp', 'toolsMeta')[name]
248
248
  } else if (type === 'prompt') {
249
- meta = pikkuState('mcp', 'promptsMeta')[name]
249
+ meta = pikkuState(null, 'mcp', 'promptsMeta')[name]
250
250
  }
251
251
 
252
252
  const result = await runPikkuFunc('mcp', `${type}:${name}`, pikkuFuncName, {
@@ -298,25 +298,25 @@ async function runMCPPikkuFunc(
298
298
  }
299
299
 
300
300
  export const getMCPTools = () => {
301
- return pikkuState('mcp', 'tools')
301
+ return pikkuState(null, 'mcp', 'tools')
302
302
  }
303
303
 
304
304
  export const getMCPResources = () => {
305
- return pikkuState('mcp', 'resources')
305
+ return pikkuState(null, 'mcp', 'resources')
306
306
  }
307
307
 
308
308
  export const getMCPResourcesMeta = () => {
309
- return pikkuState('mcp', 'resourcesMeta')
309
+ return pikkuState(null, 'mcp', 'resourcesMeta')
310
310
  }
311
311
 
312
312
  export const getMCPToolsMeta = () => {
313
- return pikkuState('mcp', 'toolsMeta')
313
+ return pikkuState(null, 'mcp', 'toolsMeta')
314
314
  }
315
315
 
316
316
  export const getMCPPrompts = () => {
317
- return pikkuState('mcp', 'prompts')
317
+ return pikkuState(null, 'mcp', 'prompts')
318
318
  }
319
319
 
320
320
  export const getMCPPromptsMeta = () => {
321
- return pikkuState('mcp', 'promptsMeta')
321
+ return pikkuState(null, 'mcp', 'promptsMeta')
322
322
  }
@@ -39,7 +39,7 @@ const createMockJob = (
39
39
  })
40
40
 
41
41
  const addTestQueueFunction = (pikkuFuncName: string) => {
42
- pikkuState('function', 'meta')[pikkuFuncName] = {
42
+ pikkuState(null, 'function', 'meta')[pikkuFuncName] = {
43
43
  pikkuFuncName,
44
44
  inputSchemaName: null,
45
45
  outputSchemaName: null,
@@ -61,14 +61,14 @@ describe('wireQueueWorker', () => {
61
61
  }
62
62
 
63
63
  // Set up metadata first
64
- pikkuState('queue', 'meta')['test-queue'] = {
64
+ pikkuState(null, 'queue', 'meta')['test-queue'] = {
65
65
  pikkuFuncName: 'queue_test-queue',
66
66
  queueName: 'test-queue',
67
67
  }
68
68
  addTestQueueFunction('queue_test-queue')
69
69
  wireQueueWorker(mockWorker)
70
70
 
71
- const registrations = pikkuState('queue', 'registrations')
71
+ const registrations = pikkuState(null, 'queue', 'registrations')
72
72
  assert.equal(registrations.has('test-queue'), true)
73
73
  assert.equal(registrations.get('test-queue'), mockWorker)
74
74
  })
@@ -109,14 +109,14 @@ describe('wireQueueWorker', () => {
109
109
  tags: ['queue'],
110
110
  }
111
111
 
112
- pikkuState('queue', 'meta')['worker-with-middleware'] = {
112
+ pikkuState(null, 'queue', 'meta')['worker-with-middleware'] = {
113
113
  pikkuFuncName: 'queue_worker-with-middleware',
114
114
  queueName: 'worker-with-middleware',
115
115
  }
116
116
  addTestQueueFunction('queue_worker-with-middleware')
117
117
  wireQueueWorker(mockWorker)
118
118
 
119
- const registrations = pikkuState('queue', 'registrations')
119
+ const registrations = pikkuState(null, 'queue', 'registrations')
120
120
  const worker = registrations.get('worker-with-middleware')
121
121
  assert.equal(worker?.middleware?.length, 1)
122
122
  assert.deepEqual(worker?.tags, ['queue'])
@@ -133,7 +133,7 @@ describe('getQueueWorkers', () => {
133
133
  },
134
134
  }
135
135
 
136
- pikkuState('queue', 'meta')['test-queue'] = {
136
+ pikkuState(null, 'queue', 'meta')['test-queue'] = {
137
137
  pikkuFuncName: 'queue_test-queue',
138
138
  queueName: 'test-queue',
139
139
  }
@@ -162,7 +162,7 @@ describe('removeQueueWorker', () => {
162
162
  },
163
163
  }
164
164
 
165
- pikkuState('queue', 'meta')['removable-queue'] = {
165
+ pikkuState(null, 'queue', 'meta')['removable-queue'] = {
166
166
  pikkuFuncName: 'queue_removable-queue',
167
167
  queueName: 'removable-queue',
168
168
  }
@@ -209,7 +209,7 @@ describe('runQueueJob', () => {
209
209
  },
210
210
  }
211
211
 
212
- pikkuState('queue', 'meta')['simple-queue'] = {
212
+ pikkuState(null, 'queue', 'meta')['simple-queue'] = {
213
213
  pikkuFuncName: 'queue_simple-queue',
214
214
  queueName: 'simple-queue',
215
215
  }
@@ -247,7 +247,7 @@ describe('runQueueJob', () => {
247
247
  },
248
248
  }
249
249
 
250
- pikkuState('queue', 'meta')['fail-queue'] = {
250
+ pikkuState(null, 'queue', 'meta')['fail-queue'] = {
251
251
  pikkuFuncName: 'queue_fail-queue',
252
252
  queueName: 'fail-queue',
253
253
  }
@@ -285,7 +285,7 @@ describe('runQueueJob', () => {
285
285
  },
286
286
  }
287
287
 
288
- pikkuState('queue', 'meta')['fail-no-reason-queue'] = {
288
+ pikkuState(null, 'queue', 'meta')['fail-no-reason-queue'] = {
289
289
  pikkuFuncName: 'queue_fail-no-reason-queue',
290
290
  queueName: 'fail-no-reason-queue',
291
291
  }
@@ -321,7 +321,7 @@ describe('runQueueJob', () => {
321
321
  },
322
322
  }
323
323
 
324
- pikkuState('queue', 'meta')['discard-queue'] = {
324
+ pikkuState(null, 'queue', 'meta')['discard-queue'] = {
325
325
  pikkuFuncName: 'queue_discard-queue',
326
326
  queueName: 'discard-queue',
327
327
  }
@@ -364,7 +364,7 @@ describe('runQueueJob', () => {
364
364
  },
365
365
  }
366
366
 
367
- pikkuState('queue', 'meta')['progress-queue'] = {
367
+ pikkuState(null, 'queue', 'meta')['progress-queue'] = {
368
368
  pikkuFuncName: 'queue_progress-queue',
369
369
  queueName: 'progress-queue',
370
370
  }
@@ -397,7 +397,7 @@ describe('runQueueJob', () => {
397
397
  },
398
398
  }
399
399
 
400
- pikkuState('queue', 'meta')['default-progress-queue'] = {
400
+ pikkuState(null, 'queue', 'meta')['default-progress-queue'] = {
401
401
  pikkuFuncName: 'queue_default-progress-queue',
402
402
  queueName: 'default-progress-queue',
403
403
  }
@@ -431,7 +431,7 @@ describe('runQueueJob', () => {
431
431
  },
432
432
  }
433
433
 
434
- pikkuState('queue', 'meta')['wire-queue'] = {
434
+ pikkuState(null, 'queue', 'meta')['wire-queue'] = {
435
435
  pikkuFuncName: 'queue_wire-queue',
436
436
  queueName: 'wire-queue',
437
437
  }
@@ -474,7 +474,7 @@ describe('runQueueJob', () => {
474
474
 
475
475
  test('should throw error when queue worker registration not found', async () => {
476
476
  // Add metadata but not registration
477
- pikkuState('queue', 'meta')['no-registration-queue'] = {
477
+ pikkuState(null, 'queue', 'meta')['no-registration-queue'] = {
478
478
  pikkuFuncName: 'queue_no-registration-queue',
479
479
  queueName: 'no-registration-queue',
480
480
  }
@@ -509,7 +509,7 @@ describe('runQueueJob', () => {
509
509
  },
510
510
  }
511
511
 
512
- pikkuState('queue', 'meta')['session-services-queue'] = {
512
+ pikkuState(null, 'queue', 'meta')['session-services-queue'] = {
513
513
  pikkuFuncName: 'queue_session-services-queue',
514
514
  queueName: 'session-services-queue',
515
515
  }
@@ -542,7 +542,7 @@ describe('runQueueJob', () => {
542
542
  },
543
543
  }
544
544
 
545
- pikkuState('queue', 'meta')['error-queue'] = {
545
+ pikkuState(null, 'queue', 'meta')['error-queue'] = {
546
546
  pikkuFuncName: 'queue_error-queue',
547
547
  queueName: 'error-queue',
548
548
  }
@@ -591,7 +591,7 @@ describe('runQueueJob', () => {
591
591
  middleware: [middleware],
592
592
  }
593
593
 
594
- pikkuState('queue', 'meta')['middleware-queue'] = {
594
+ pikkuState(null, 'queue', 'meta')['middleware-queue'] = {
595
595
  pikkuFuncName: 'queue_middleware-queue',
596
596
  queueName: 'middleware-queue',
597
597
  }
@@ -618,7 +618,7 @@ describe('runQueueJob', () => {
618
618
  },
619
619
  }
620
620
 
621
- pikkuState('queue', 'meta')['debug-queue'] = {
621
+ pikkuState(null, 'queue', 'meta')['debug-queue'] = {
622
622
  pikkuFuncName: 'queue_debug-queue',
623
623
  queueName: 'debug-queue',
624
624
  }
@@ -52,7 +52,7 @@ export const wireQueueWorker = <
52
52
  queueWorker: CoreQueueWorker<PikkuFunctionConfig>
53
53
  ) => {
54
54
  // Get processor metadata
55
- const meta = pikkuState('queue', 'meta')
55
+ const meta = pikkuState(null, 'queue', 'meta')
56
56
  const processorMeta = meta[queueWorker.queueName]
57
57
  if (!processorMeta) {
58
58
  throw new Error(
@@ -70,7 +70,7 @@ export const wireQueueWorker = <
70
70
  })
71
71
 
72
72
  // Store processor definition in state - runtime adapters will pick this up
73
- const registrations = pikkuState('queue', 'registrations')
73
+ const registrations = pikkuState(null, 'queue', 'registrations')
74
74
  registrations.set(queueWorker.queueName, queueWorker)
75
75
  }
76
76
 
@@ -78,14 +78,14 @@ export const wireQueueWorker = <
78
78
  * Get all registered queue processors
79
79
  */
80
80
  export function getQueueWorkers(): Map<string, CoreQueueWorker> {
81
- return pikkuState('queue', 'registrations')
81
+ return pikkuState(null, 'queue', 'registrations')
82
82
  }
83
83
 
84
84
  /**
85
85
  * Stop and remove a queue processor
86
86
  */
87
87
  export async function removeQueueWorker(name: string): Promise<void> {
88
- const registrations = pikkuState('queue', 'registrations')
88
+ const registrations = pikkuState(null, 'queue', 'registrations')
89
89
  const registration = registrations.get(name)
90
90
 
91
91
  if (!registration) {
@@ -111,14 +111,14 @@ export async function runQueueJob({
111
111
  }): Promise<void> {
112
112
  const logger = singletonServices.logger
113
113
 
114
- const meta = pikkuState('queue', 'meta')
114
+ const meta = pikkuState(null, 'queue', 'meta')
115
115
  const processorMeta = meta[job.queueName]
116
116
  if (!processorMeta) {
117
117
  throw new Error(`Processor metadata not found for: ${job.queueName}`)
118
118
  }
119
119
 
120
120
  // Get the queue worker registration to access middleware
121
- const registrations = pikkuState('queue', 'registrations')
121
+ const registrations = pikkuState(null, 'queue', 'registrations')
122
122
  const queueWorker = registrations.get(job.queueName)
123
123
  if (!queueWorker) {
124
124
  throw new Error(`Queue worker registration not found for: ${job.queueName}`)
@@ -1,2 +1,2 @@
1
- export { initialize, PikkuRPCService, rpcService } from './rpc-runner.js'
1
+ export { PikkuRPCService, rpcService } from './rpc-runner.js'
2
2
  export type { PikkuRPC, RPCMeta } from './rpc-types.js'
@@ -2,15 +2,37 @@ import { CoreServices, PikkuWire } from '../../types/core.types.js'
2
2
  import { runPikkuFunc } from '../../function/function-runner.js'
3
3
  import { pikkuState } from '../../pikku-state.js'
4
4
  import { ForbiddenError } from '../../errors/errors.js'
5
- import { PikkuRPC } from './rpc-types.js'
5
+ import { PikkuRPC, ResolvedFunction } from './rpc-types.js'
6
6
 
7
- // Type for the RPC service configuration
8
- type RPCServiceConfig = {
9
- coerceDataFromSchema: boolean
7
+ /**
8
+ * Resolve a namespaced function reference to package and function names
9
+ * Uses pikkuState to look up the namespace -> package mapping
10
+ */
11
+ const resolveNamespace = (
12
+ namespacedFunction: string
13
+ ): ResolvedFunction | null => {
14
+ const colonIndex = namespacedFunction.indexOf(':')
15
+ if (colonIndex === -1) {
16
+ return null
17
+ }
18
+
19
+ const namespace = namespacedFunction.substring(0, colonIndex)
20
+ const functionName = namespacedFunction.substring(colonIndex + 1)
21
+
22
+ const externalPackages = pikkuState(null, 'rpc', 'externalPackages')
23
+ const packageName = externalPackages.get(namespace)
24
+ if (!packageName) {
25
+ return null
26
+ }
27
+
28
+ return {
29
+ package: packageName,
30
+ function: functionName,
31
+ }
10
32
  }
11
33
 
12
34
  const getPikkuFunctionName = (rpcName: string): string => {
13
- const rpc = pikkuState('rpc', 'meta')
35
+ const rpc = pikkuState(null, 'rpc', 'meta')
14
36
  const rpcMeta = rpc[rpcName]
15
37
  if (!rpcMeta) {
16
38
  throw new Error(`RPC function not found: ${rpcName}`)
@@ -24,13 +46,12 @@ export class ContextAwareRPCService {
24
46
  private services: CoreServices,
25
47
  private wire: PikkuWire,
26
48
  private options: {
27
- coerceDataFromSchema?: boolean
28
49
  requiresAuth?: boolean
29
50
  }
30
51
  ) {}
31
52
 
32
53
  public async rpcExposed(funcName: string, data: any): Promise<any> {
33
- const functionMeta = pikkuState('function', 'meta')[funcName]
54
+ const functionMeta = pikkuState(null, 'function', 'meta')[funcName]
34
55
  if (!functionMeta) {
35
56
  throw new Error(`Function not found: ${funcName}`)
36
57
  }
@@ -55,6 +76,17 @@ export class ContextAwareRPCService {
55
76
  }
56
77
  : undefined,
57
78
  }
79
+
80
+ // Check if it's a namespaced function call (e.g., 'stripe:createCharge')
81
+ if (funcName.includes(':')) {
82
+ return this.invokeExternalPackageFunction<In, Out>(
83
+ funcName,
84
+ data,
85
+ updatedWire
86
+ )
87
+ }
88
+
89
+ // Main package function
58
90
  return runPikkuFunc<In, Out>(
59
91
  'rpc',
60
92
  funcName,
@@ -65,12 +97,60 @@ export class ContextAwareRPCService {
65
97
  // but is valid since we don't want to keep creating new wire services
66
98
  singletonServices: this.services,
67
99
  data: () => data,
68
- coerceDataFromSchema: this.options.coerceDataFromSchema,
69
100
  wire: updatedWire,
70
101
  }
71
102
  )
72
103
  }
73
104
 
105
+ /**
106
+ * Invoke a function from an external package
107
+ * External packages register their functions in pikkuState under their package name.
108
+ * The function is executed using the parent services (shared singleton services).
109
+ * @private
110
+ */
111
+ private async invokeExternalPackageFunction<In = any, Out = any>(
112
+ namespacedFunction: string,
113
+ data: In,
114
+ wire: PikkuWire
115
+ ): Promise<Out> {
116
+ // Resolve namespace to package name
117
+ const resolved = resolveNamespace(namespacedFunction)
118
+ if (!resolved) {
119
+ throw new Error(
120
+ `Unknown namespace in function reference: ${namespacedFunction}. ` +
121
+ `Make sure the package is registered in externalPackages config.`
122
+ )
123
+ }
124
+
125
+ // Get the function meta from the external package
126
+ // External packages use function meta, not RPC meta
127
+ const externalFunctionMeta = pikkuState(
128
+ resolved.package,
129
+ 'function',
130
+ 'meta'
131
+ )
132
+ const funcMeta = externalFunctionMeta[resolved.function]
133
+ if (!funcMeta) {
134
+ throw new Error(
135
+ `Function '${resolved.function}' not found in package '${resolved.package}'. ` +
136
+ `Available functions: ${Object.keys(externalFunctionMeta).join(', ') || '(none)'}`
137
+ )
138
+ }
139
+ const funcName = funcMeta.pikkuFuncName || resolved.function
140
+
141
+ // Execute the function using runPikkuFunc with the external package's state
142
+ // We use the parent services (this.services) since external packages share services
143
+ // Pass the function's tags so tag-based middleware/permissions are applied
144
+ return runPikkuFunc<In, Out>('rpc', namespacedFunction, funcName, {
145
+ auth: this.options.requiresAuth,
146
+ singletonServices: this.services,
147
+ data: () => data,
148
+ wire,
149
+ packageName: resolved.package,
150
+ tags: funcMeta.tags,
151
+ })
152
+ }
153
+
74
154
  public async rpcWithWire<In = any, Out = any>(
75
155
  rpcName: string,
76
156
  data: In,
@@ -96,7 +176,6 @@ export class ContextAwareRPCService {
96
176
  auth: this.options.requiresAuth,
97
177
  singletonServices: this.services,
98
178
  data: () => data,
99
- coerceDataFromSchema: this.options.coerceDataFromSchema,
100
179
  wire: mergedWire,
101
180
  }
102
181
  )
@@ -122,13 +201,6 @@ export class PikkuRPCService<
122
201
  Services extends CoreServices,
123
202
  TypedRPC = PikkuRPC,
124
203
  > {
125
- private config?: RPCServiceConfig
126
-
127
- // Initialize the RPC service with configuration
128
- initialize(config: RPCServiceConfig) {
129
- this.config = config
130
- }
131
-
132
204
  // Convenience function for initializing
133
205
  getContextRPCService(
134
206
  services: Services,
@@ -137,7 +209,6 @@ export class PikkuRPCService<
137
209
  depth: number = 0
138
210
  ): TypedRPC {
139
211
  const serviceRPC = new ContextAwareRPCService(services, wire, {
140
- coerceDataFromSchema: this.config?.coerceDataFromSchema,
141
212
  requiresAuth,
142
213
  })
143
214
  return {
@@ -153,8 +224,3 @@ export class PikkuRPCService<
153
224
 
154
225
  // Create a singleton instance
155
226
  export const rpcService = new PikkuRPCService()
156
-
157
- // Convenience function for initializing
158
- export const initialize = (config: RPCServiceConfig) => {
159
- rpcService.initialize(config)
160
- }
@@ -17,3 +17,11 @@ export interface RPCHandler<Input, Output> {
17
17
  input: Input
18
18
  output: Output
19
19
  }
20
+
21
+ /**
22
+ * Resolved function reference from namespace
23
+ */
24
+ export interface ResolvedFunction {
25
+ package: string
26
+ function: string
27
+ }
@@ -6,7 +6,7 @@ import { Logger } from '../../services/index.js'
6
6
  * @param logger - A logger for logging information.
7
7
  */
8
8
  export const logSchedulers = (logger: Logger) => {
9
- const scheduledTasks = pikkuState('scheduler', 'tasks')
9
+ const scheduledTasks = pikkuState(null, 'scheduler', 'tasks')
10
10
  if (scheduledTasks.size === 0) {
11
11
  logger.info('No scheduled tasks added')
12
12
  return