@pikku/inspector 0.11.2 → 0.12.0

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 (182) hide show
  1. package/CHANGELOG.md +11 -1
  2. package/OPTIMIZATION-PLAN.md +195 -0
  3. package/dist/add/add-ai-agent.d.ts +2 -0
  4. package/dist/add/add-ai-agent.js +314 -0
  5. package/dist/add/add-channel.js +69 -61
  6. package/dist/add/add-cli.js +36 -18
  7. package/dist/add/add-file-with-factory.js +2 -0
  8. package/dist/add/add-functions.js +250 -75
  9. package/dist/add/add-http-route.d.ts +19 -10
  10. package/dist/add/add-http-route.js +152 -66
  11. package/dist/add/add-http-routes.d.ts +5 -0
  12. package/dist/add/add-http-routes.js +159 -0
  13. package/dist/add/add-keyed-wiring.d.ts +12 -0
  14. package/dist/add/add-keyed-wiring.js +97 -0
  15. package/dist/add/add-mcp-prompt.js +14 -9
  16. package/dist/add/add-mcp-resource.js +14 -9
  17. package/dist/add/add-middleware.d.ts +1 -4
  18. package/dist/add/add-middleware.js +364 -79
  19. package/dist/add/add-permission.d.ts +1 -1
  20. package/dist/add/add-permission.js +152 -40
  21. package/dist/add/add-queue-worker.js +18 -12
  22. package/dist/add/add-rpc-invocations.js +14 -0
  23. package/dist/add/add-schedule.js +11 -5
  24. package/dist/add/add-secret.d.ts +3 -0
  25. package/dist/add/add-secret.js +82 -0
  26. package/dist/add/add-trigger.d.ts +2 -0
  27. package/dist/add/add-trigger.js +87 -0
  28. package/dist/add/add-variable.d.ts +1 -0
  29. package/dist/add/add-variable.js +8 -0
  30. package/dist/add/add-workflow-graph.d.ts +3 -2
  31. package/dist/add/add-workflow-graph.js +143 -406
  32. package/dist/add/add-workflow.js +6 -4
  33. package/dist/error-codes.d.ts +14 -1
  34. package/dist/error-codes.js +19 -1
  35. package/dist/index.d.ts +9 -8
  36. package/dist/index.js +5 -4
  37. package/dist/inspector.d.ts +1 -1
  38. package/dist/inspector.js +91 -14
  39. package/dist/schema-generator.d.ts +1 -0
  40. package/dist/schema-generator.js +1 -0
  41. package/dist/types-map.js +10 -1
  42. package/dist/types.d.ts +163 -39
  43. package/dist/utils/compute-required-schemas.d.ts +4 -0
  44. package/dist/utils/compute-required-schemas.js +41 -0
  45. package/dist/utils/contract-hashes.d.ts +35 -0
  46. package/dist/utils/contract-hashes.js +202 -0
  47. package/dist/utils/custom-types-generator.d.ts +9 -0
  48. package/dist/utils/custom-types-generator.js +71 -0
  49. package/dist/utils/detect-schema-vendor.d.ts +22 -0
  50. package/dist/utils/detect-schema-vendor.js +76 -0
  51. package/dist/utils/ensure-function-metadata.d.ts +5 -2
  52. package/dist/utils/ensure-function-metadata.js +220 -6
  53. package/dist/utils/extract-function-name.d.ts +5 -16
  54. package/dist/utils/extract-function-name.js +86 -291
  55. package/dist/utils/extract-services.d.ts +2 -1
  56. package/dist/utils/extract-services.js +25 -1
  57. package/dist/utils/filter-inspector-state.js +107 -23
  58. package/dist/utils/get-property-value.d.ts +6 -1
  59. package/dist/utils/get-property-value.js +28 -3
  60. package/dist/utils/hash.d.ts +2 -0
  61. package/dist/utils/hash.js +23 -0
  62. package/dist/utils/middleware.d.ts +7 -30
  63. package/dist/utils/middleware.js +80 -66
  64. package/dist/utils/permissions.d.ts +2 -2
  65. package/dist/utils/permissions.js +10 -10
  66. package/dist/utils/post-process.d.ts +9 -10
  67. package/dist/utils/post-process.js +231 -24
  68. package/dist/utils/resolve-external-package.d.ts +12 -0
  69. package/dist/utils/resolve-external-package.js +34 -0
  70. package/dist/utils/resolve-function-types.d.ts +6 -0
  71. package/dist/utils/resolve-function-types.js +29 -0
  72. package/dist/utils/resolve-identifier.d.ts +10 -0
  73. package/dist/utils/resolve-identifier.js +36 -0
  74. package/dist/utils/resolve-versions.d.ts +2 -0
  75. package/dist/utils/resolve-versions.js +78 -0
  76. package/dist/utils/schema-generator.d.ts +9 -0
  77. package/dist/utils/schema-generator.js +209 -0
  78. package/dist/utils/serialize-inspector-state.d.ts +59 -22
  79. package/dist/utils/serialize-inspector-state.js +92 -20
  80. package/dist/utils/serialize-mcp-json.d.ts +2 -0
  81. package/dist/utils/serialize-mcp-json.js +99 -0
  82. package/dist/utils/serialize-middleware-groups-meta.d.ts +12 -0
  83. package/dist/utils/serialize-middleware-groups-meta.js +28 -0
  84. package/dist/utils/serialize-openapi-json.d.ts +85 -0
  85. package/dist/utils/serialize-openapi-json.js +151 -0
  86. package/dist/utils/serialize-permissions-groups-meta.d.ts +6 -0
  87. package/dist/utils/serialize-permissions-groups-meta.js +31 -0
  88. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +34 -102
  89. package/dist/utils/workflow/dsl/extract-dsl-workflow.js +23 -4
  90. package/dist/utils/workflow/graph/convert-dsl-to-graph.js +12 -10
  91. package/dist/utils/workflow/graph/finalize-workflow-wires.d.ts +3 -0
  92. package/dist/utils/workflow/graph/finalize-workflow-wires.js +276 -0
  93. package/dist/utils/workflow/graph/finalize-workflows.d.ts +2 -0
  94. package/dist/utils/workflow/graph/finalize-workflows.js +75 -0
  95. package/dist/utils/workflow/graph/index.d.ts +2 -0
  96. package/dist/utils/workflow/graph/index.js +2 -0
  97. package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +0 -8
  98. package/dist/utils/workflow/graph/serialize-workflow-graph.js +1 -3
  99. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +53 -79
  100. package/dist/utils/workflow/graph/workflow-graph.types.js +1 -1
  101. package/dist/visit.js +11 -6
  102. package/package.json +14 -4
  103. package/src/add/add-ai-agent.ts +468 -0
  104. package/src/add/add-channel.ts +82 -79
  105. package/src/add/add-cli.ts +49 -20
  106. package/src/add/add-file-with-factory.ts +2 -0
  107. package/src/add/add-functions.ts +330 -86
  108. package/src/add/add-http-route.ts +245 -88
  109. package/src/add/add-http-routes.ts +228 -0
  110. package/src/add/add-keyed-wiring.ts +151 -0
  111. package/src/add/add-mcp-prompt.ts +26 -15
  112. package/src/add/add-mcp-resource.ts +27 -15
  113. package/src/add/add-middleware.ts +482 -80
  114. package/src/add/add-permission.ts +199 -40
  115. package/src/add/add-queue-worker.ts +24 -19
  116. package/src/add/add-rpc-invocations.ts +17 -0
  117. package/src/add/add-schedule.ts +16 -11
  118. package/src/add/add-secret.ts +140 -0
  119. package/src/add/add-trigger.ts +154 -0
  120. package/src/add/add-variable.ts +9 -0
  121. package/src/add/add-workflow-graph.ts +180 -522
  122. package/src/add/add-workflow.ts +5 -4
  123. package/src/error-codes.ts +24 -1
  124. package/src/index.ts +22 -13
  125. package/src/inspector.ts +129 -17
  126. package/src/schema-generator.ts +1 -0
  127. package/src/types-map.ts +12 -1
  128. package/src/types.ts +175 -58
  129. package/src/utils/compute-required-schemas.ts +49 -0
  130. package/src/utils/contract-hashes.test.ts +528 -0
  131. package/src/utils/contract-hashes.ts +290 -0
  132. package/src/utils/custom-types-generator.ts +88 -0
  133. package/src/utils/detect-schema-vendor.ts +90 -0
  134. package/src/utils/ensure-function-metadata.ts +324 -7
  135. package/src/utils/extract-function-name.ts +101 -351
  136. package/src/utils/extract-services.ts +35 -2
  137. package/src/utils/filter-inspector-state.test.ts +34 -20
  138. package/src/utils/filter-inspector-state.ts +140 -31
  139. package/src/utils/get-property-value.ts +42 -4
  140. package/src/utils/hash.ts +26 -0
  141. package/src/utils/middleware.test.ts +204 -0
  142. package/src/utils/middleware.ts +129 -67
  143. package/src/utils/permissions.test.ts +35 -12
  144. package/src/utils/permissions.ts +10 -10
  145. package/src/utils/post-process.ts +283 -43
  146. package/src/utils/resolve-external-package.ts +42 -0
  147. package/src/utils/resolve-function-types.ts +42 -0
  148. package/src/utils/resolve-identifier.ts +46 -0
  149. package/src/utils/resolve-versions.test.ts +249 -0
  150. package/src/utils/resolve-versions.ts +105 -0
  151. package/src/utils/schema-generator.ts +329 -0
  152. package/src/utils/serialize-inspector-state.ts +163 -40
  153. package/src/utils/serialize-mcp-json.ts +145 -0
  154. package/src/utils/serialize-middleware-groups-meta.ts +33 -0
  155. package/src/utils/serialize-openapi-json.ts +277 -0
  156. package/src/utils/serialize-permissions-groups-meta.ts +35 -0
  157. package/src/utils/test-data/inspector-state.json +69 -66
  158. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +43 -119
  159. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +24 -4
  160. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +17 -10
  161. package/src/utils/workflow/graph/finalize-workflow-wires.ts +310 -0
  162. package/src/utils/workflow/graph/finalize-workflows.ts +100 -0
  163. package/src/utils/workflow/graph/index.ts +5 -0
  164. package/src/utils/workflow/graph/serialize-workflow-graph.ts +1 -8
  165. package/src/utils/workflow/graph/workflow-graph.types.ts +29 -78
  166. package/src/visit.ts +12 -6
  167. package/tsconfig.tsbuildinfo +1 -1
  168. package/dist/add/add-forge-credential.d.ts +0 -8
  169. package/dist/add/add-forge-credential.js +0 -77
  170. package/dist/add/add-forge-node.d.ts +0 -7
  171. package/dist/add/add-forge-node.js +0 -77
  172. package/dist/add/add-mcp-tool.d.ts +0 -2
  173. package/dist/add/add-mcp-tool.js +0 -81
  174. package/dist/utils/extract-service-metadata.d.ts +0 -19
  175. package/dist/utils/extract-service-metadata.js +0 -244
  176. package/dist/utils/write-service-metadata.d.ts +0 -13
  177. package/dist/utils/write-service-metadata.js +0 -37
  178. package/src/add/add-forge-credential.ts +0 -119
  179. package/src/add/add-forge-node.ts +0 -132
  180. package/src/add/add-mcp-tool.ts +0 -141
  181. package/src/utils/extract-service-metadata.ts +0 -353
  182. package/src/utils/write-service-metadata.ts +0 -51
@@ -1,9 +1,38 @@
1
1
  import * as ts from 'typescript';
2
- import { extractFunctionName, isNamedExport, } from '../utils/extract-function-name.js';
3
- import { extractServicesFromFunction } from '../utils/extract-services.js';
2
+ import { extractFunctionName, isNamedExport, makeContextBasedId, } from '../utils/extract-function-name.js';
3
+ import { extractServicesFromFunction, extractUsedWires, } from '../utils/extract-services.js';
4
4
  import { extractPermissionPikkuNames } from '../utils/permissions.js';
5
5
  import { getPropertyValue } from '../utils/get-property-value.js';
6
6
  import { getPropertyAssignmentInitializer } from '../utils/type-utils.js';
7
+ function renameTempDefinitions(state, definitionIds, groupType, groupKey) {
8
+ const tempIndices = definitionIds
9
+ .map((name, i) => (name.startsWith('__temp_') ? i : -1))
10
+ .filter((i) => i >= 0);
11
+ for (const idx of tempIndices) {
12
+ const oldId = definitionIds[idx];
13
+ const newId = tempIndices.length === 1
14
+ ? makeContextBasedId(groupType, groupKey)
15
+ : makeContextBasedId(groupType, groupKey, String(idx));
16
+ const existing = state.permissions.definitions[oldId];
17
+ if (existing) {
18
+ delete state.permissions.definitions[oldId];
19
+ state.permissions.definitions[newId] = existing;
20
+ }
21
+ definitionIds[idx] = newId;
22
+ }
23
+ }
24
+ function isInsidePermissionFactory(node) {
25
+ let current = node.parent;
26
+ while (current) {
27
+ if (ts.isCallExpression(current) &&
28
+ ts.isIdentifier(current.expression) &&
29
+ current.expression.text === 'pikkuPermissionFactory') {
30
+ return true;
31
+ }
32
+ current = current.parent;
33
+ }
34
+ return false;
35
+ }
7
36
  /**
8
37
  * Inspect pikkuPermission calls, addPermission calls, and addHTTPPermission calls
9
38
  */
@@ -17,20 +46,20 @@ export const addPermission = (logger, node, checker, state) => {
17
46
  }
18
47
  // Handle pikkuPermission(...) - individual permission function definition
19
48
  if (expression.text === 'pikkuPermission') {
49
+ // Skip if nested inside pikkuPermissionFactory — the factory handler extracts services itself
50
+ if (isInsidePermissionFactory(node))
51
+ return;
20
52
  const arg = args[0];
21
53
  if (!arg)
22
54
  return;
23
55
  let actualHandler;
24
56
  let name;
25
57
  let description;
26
- // Check if using object syntax: pikkuPermission({ func: ..., name: '...', description: '...' })
27
58
  if (ts.isObjectLiteralExpression(arg)) {
28
- // Extract name and description metadata
29
59
  const nameValue = getPropertyValue(arg, 'name');
30
60
  const descValue = getPropertyValue(arg, 'description');
31
61
  name = typeof nameValue === 'string' ? nameValue : undefined;
32
62
  description = typeof descValue === 'string' ? descValue : undefined;
33
- // Extract the func property
34
63
  const fnProp = getPropertyAssignmentInitializer(arg, 'func', true, checker);
35
64
  if (!fnProp ||
36
65
  (!ts.isArrowFunction(fnProp) && !ts.isFunctionExpression(fnProp))) {
@@ -47,16 +76,97 @@ export const addPermission = (logger, node, checker, state) => {
47
76
  return;
48
77
  }
49
78
  const services = extractServicesFromFunction(actualHandler);
50
- const { pikkuFuncName, exportedName } = extractFunctionName(node, checker, state.rootDir);
51
- state.permissions.meta[pikkuFuncName] = {
79
+ const wires = extractUsedWires(actualHandler, 2);
80
+ let { pikkuFuncId, exportedName } = extractFunctionName(node, checker, state.rootDir);
81
+ if (pikkuFuncId.startsWith('__temp_')) {
82
+ if (ts.isVariableDeclaration(node.parent) &&
83
+ ts.isIdentifier(node.parent.name)) {
84
+ pikkuFuncId = node.parent.name.text;
85
+ }
86
+ else if (ts.isPropertyAssignment(node.parent) &&
87
+ ts.isIdentifier(node.parent.name)) {
88
+ pikkuFuncId = node.parent.name.text;
89
+ }
90
+ else {
91
+ logger.error(`• pikkuPermission() must be assigned to a variable or object property. ` +
92
+ `Extract it to a const: const myPermission = pikkuPermission(...)`);
93
+ return;
94
+ }
95
+ }
96
+ const dataParam = actualHandler.parameters[1];
97
+ const dataParamName = dataParam && ts.isIdentifier(dataParam.name) ? dataParam.name.text : null;
98
+ const requiresData = !(dataParamName && dataParamName.startsWith('_'));
99
+ state.permissions.definitions[pikkuFuncId] = {
100
+ services,
101
+ wires: wires.wires.length > 0 || !wires.optimized ? wires : undefined,
102
+ sourceFile: node.getSourceFile().fileName,
103
+ position: node.getStart(),
104
+ exportedName,
105
+ name,
106
+ description,
107
+ ...(requiresData ? {} : { requiresData: false }),
108
+ };
109
+ logger.debug(`• Found permission with services: ${services.services.join(', ')}${name ? ` (name: ${name})` : ''}${description ? ` (description: ${description})` : ''}${!requiresData ? ' (auth-only)' : ''}`);
110
+ return;
111
+ }
112
+ if (expression.text === 'pikkuAuth') {
113
+ if (isInsidePermissionFactory(node))
114
+ return;
115
+ const arg = args[0];
116
+ if (!arg)
117
+ return;
118
+ let actualHandler;
119
+ let name;
120
+ let description;
121
+ if (ts.isObjectLiteralExpression(arg)) {
122
+ const nameValue = getPropertyValue(arg, 'name');
123
+ const descValue = getPropertyValue(arg, 'description');
124
+ name = typeof nameValue === 'string' ? nameValue : undefined;
125
+ description = typeof descValue === 'string' ? descValue : undefined;
126
+ const fnProp = getPropertyAssignmentInitializer(arg, 'func', true, checker);
127
+ if (!fnProp ||
128
+ (!ts.isArrowFunction(fnProp) && !ts.isFunctionExpression(fnProp))) {
129
+ logger.error(`• pikkuAuth object missing required 'func' property.`);
130
+ return;
131
+ }
132
+ actualHandler = fnProp;
133
+ }
134
+ else if (ts.isArrowFunction(arg) || ts.isFunctionExpression(arg)) {
135
+ actualHandler = arg;
136
+ }
137
+ else {
138
+ logger.error(`• Handler for pikkuAuth is not a function.`);
139
+ return;
140
+ }
141
+ const services = extractServicesFromFunction(actualHandler);
142
+ const wires = extractUsedWires(actualHandler, 1);
143
+ let { pikkuFuncId, exportedName } = extractFunctionName(node, checker, state.rootDir);
144
+ if (pikkuFuncId.startsWith('__temp_')) {
145
+ if (ts.isVariableDeclaration(node.parent) &&
146
+ ts.isIdentifier(node.parent.name)) {
147
+ pikkuFuncId = node.parent.name.text;
148
+ }
149
+ else if (ts.isPropertyAssignment(node.parent) &&
150
+ ts.isIdentifier(node.parent.name)) {
151
+ pikkuFuncId = node.parent.name.text;
152
+ }
153
+ else {
154
+ logger.error(`• pikkuAuth() must be assigned to a variable or object property. ` +
155
+ `Extract it to a const: const myAuth = pikkuAuth(...)`);
156
+ return;
157
+ }
158
+ }
159
+ state.permissions.definitions[pikkuFuncId] = {
52
160
  services,
161
+ wires: wires.wires.length > 0 || !wires.optimized ? wires : undefined,
53
162
  sourceFile: node.getSourceFile().fileName,
54
163
  position: node.getStart(),
55
164
  exportedName,
56
165
  name,
57
166
  description,
167
+ requiresData: false,
58
168
  };
59
- logger.debug(`• Found permission with services: ${services.services.join(', ')}${name ? ` (name: ${name})` : ''}${description ? ` (description: ${description})` : ''}`);
169
+ logger.debug(`• Found auth permission with services: ${services.services.join(', ')}${name ? ` (name: ${name})` : ''}${description ? ` (description: ${description})` : ''}`);
60
170
  return;
61
171
  }
62
172
  // Handle pikkuPermissionFactory(...) - permission factory function
@@ -73,6 +183,10 @@ export const addPermission = (logger, node, checker, state) => {
73
183
  // The factory should return pikkuPermission(...), so we need to find that call
74
184
  // If no wrapper is found, extract from the factory's returned function directly
75
185
  let services = { optimized: false, services: [] };
186
+ let wires = {
187
+ optimized: true,
188
+ wires: [],
189
+ };
76
190
  const findPikkuPermissionCall = (node) => {
77
191
  if (ts.isCallExpression(node)) {
78
192
  const expr = node.expression;
@@ -88,24 +202,39 @@ export const addPermission = (logger, node, checker, state) => {
88
202
  if (ts.isArrowFunction(permissionHandler) ||
89
203
  ts.isFunctionExpression(permissionHandler)) {
90
204
  services = extractServicesFromFunction(permissionHandler);
205
+ wires = extractUsedWires(permissionHandler, 2);
91
206
  }
92
207
  }
93
208
  else {
94
- // No pikkuPermission wrapper found - extract from factory's return value directly
95
- // Factory pattern: (config) => (services, data, wire) => { ... }
96
209
  if (ts.isArrowFunction(factoryNode) ||
97
210
  ts.isFunctionExpression(factoryNode)) {
98
211
  const factoryBody = factoryNode.body;
99
- // Check if the body is an arrow function (direct return)
100
212
  if (ts.isArrowFunction(factoryBody) ||
101
213
  ts.isFunctionExpression(factoryBody)) {
102
214
  services = extractServicesFromFunction(factoryBody);
215
+ wires = extractUsedWires(factoryBody, 2);
103
216
  }
104
217
  }
105
218
  }
106
- const { pikkuFuncName, exportedName } = extractFunctionName(node, checker, state.rootDir);
107
- state.permissions.meta[pikkuFuncName] = {
219
+ let { pikkuFuncId, exportedName } = extractFunctionName(node, checker, state.rootDir);
220
+ if (pikkuFuncId.startsWith('__temp_')) {
221
+ if (ts.isVariableDeclaration(node.parent) &&
222
+ ts.isIdentifier(node.parent.name)) {
223
+ pikkuFuncId = node.parent.name.text;
224
+ }
225
+ else if (ts.isPropertyAssignment(node.parent) &&
226
+ ts.isIdentifier(node.parent.name)) {
227
+ pikkuFuncId = node.parent.name.text;
228
+ }
229
+ else {
230
+ logger.error(`• pikkuPermissionFactory() must be assigned to a variable or object property. ` +
231
+ `Extract it to a const: const myPermission = pikkuPermissionFactory(...)`);
232
+ return;
233
+ }
234
+ }
235
+ state.permissions.definitions[pikkuFuncId] = {
108
236
  services,
237
+ wires: wires.wires.length > 0 || !wires.optimized ? wires : undefined,
109
238
  sourceFile: node.getSourceFile().fileName,
110
239
  position: node.getStart(),
111
240
  exportedName,
@@ -138,38 +267,31 @@ export const addPermission = (logger, node, checker, state) => {
138
267
  logger.error(`• addPermission('${tag}', ...) must have a literal array or object as second argument`);
139
268
  return;
140
269
  }
141
- // Extract permission pikkuFuncNames from array
270
+ // Extract permission pikkuFuncIds from array
142
271
  const permissionNames = extractPermissionPikkuNames(permissionsArrayArg, checker, state.rootDir);
143
272
  if (permissionNames.length === 0) {
144
273
  logger.warn(`• addPermission('${tag}', ...) has empty permissions array`);
145
274
  return;
146
275
  }
147
- // Collect services from all permissions in the group
276
+ renameTempDefinitions(state, permissionNames, 'tag', tag);
148
277
  const allServices = new Set();
149
278
  for (const permissionName of permissionNames) {
150
- const permissionMeta = state.permissions.meta[permissionName];
279
+ const permissionMeta = state.permissions.definitions[permissionName];
151
280
  if (permissionMeta && permissionMeta.services) {
152
281
  for (const service of permissionMeta.services.services) {
153
282
  allServices.add(service);
154
283
  }
155
284
  }
156
285
  }
157
- // Check if this call is wrapped in a factory function
158
- // We need to walk up the tree to see if the parent is: const x = () => addPermission(...)
159
286
  let isFactory = false;
160
287
  let exportedName = null;
161
288
  let parent = node.parent;
162
- // Check if parent is arrow function: () => addPermission(...)
163
289
  if (parent && ts.isArrowFunction(parent)) {
164
- // Check if arrow function has no parameters
165
290
  if (parent.parameters.length === 0) {
166
291
  isFactory = true;
167
- // For factories, we need to check the arrow function's parent for the export name
168
- // const apiTagPermissions = () => addPermission(...)
169
292
  const arrowParent = parent.parent;
170
293
  if (arrowParent && ts.isVariableDeclaration(arrowParent)) {
171
294
  if (ts.isIdentifier(arrowParent.name)) {
172
- // Check if it's exported
173
295
  if (isNamedExport(arrowParent)) {
174
296
  exportedName = arrowParent.name.text;
175
297
  }
@@ -177,17 +299,14 @@ export const addPermission = (logger, node, checker, state) => {
177
299
  }
178
300
  }
179
301
  }
180
- // If not a factory, get export name from the call expression itself
181
302
  if (!isFactory) {
182
303
  const extracted = extractFunctionName(node, checker, state.rootDir);
183
304
  exportedName = extracted.exportedName;
184
305
  }
185
- // Log warning if not using factory pattern
186
306
  if (!isFactory && exportedName) {
187
307
  logger.warn(`• Permission group '${exportedName}' for tag '${tag}' is not wrapped in a factory function. ` +
188
308
  `For tree-shaking, use: export const ${exportedName} = () => addPermission('${tag}', [...])`);
189
309
  }
190
- // Store group metadata
191
310
  state.permissions.tagPermissions.set(tag, {
192
311
  exportName: exportedName,
193
312
  sourceFile: node.getSourceFile().fileName,
@@ -196,7 +315,8 @@ export const addPermission = (logger, node, checker, state) => {
196
315
  optimized: false,
197
316
  services: Array.from(allServices),
198
317
  },
199
- permissionCount: permissionNames.length,
318
+ count: permissionNames.length,
319
+ instanceIds: permissionNames,
200
320
  isFactory,
201
321
  });
202
322
  logger.debug(`• Found tag permission group: ${tag} -> [${permissionNames.join(', ')}] (${isFactory ? 'factory' : 'direct'})`);
@@ -226,37 +346,31 @@ export const addPermission = (logger, node, checker, state) => {
226
346
  logger.error(`• addHTTPPermission('${pattern}', ...) must have a literal array or object as second argument`);
227
347
  return;
228
348
  }
229
- // Extract permission pikkuFuncNames from array
349
+ // Extract permission pikkuFuncIds from array
230
350
  const permissionNames = extractPermissionPikkuNames(permissionsArrayArg, checker, state.rootDir);
231
351
  if (permissionNames.length === 0) {
232
352
  logger.warn(`• addHTTPPermission('${pattern}', ...) has empty permissions array`);
233
353
  return;
234
354
  }
235
- // Collect services from all permissions in the group
355
+ renameTempDefinitions(state, permissionNames, 'http', pattern);
236
356
  const allServices = new Set();
237
357
  for (const permissionName of permissionNames) {
238
- const permissionMeta = state.permissions.meta[permissionName];
358
+ const permissionMeta = state.permissions.definitions[permissionName];
239
359
  if (permissionMeta && permissionMeta.services) {
240
360
  for (const service of permissionMeta.services.services) {
241
361
  allServices.add(service);
242
362
  }
243
363
  }
244
364
  }
245
- // Check if this call is wrapped in a factory function
246
365
  let isFactory = false;
247
366
  let exportedName = null;
248
367
  let parent = node.parent;
249
- // Check if parent is arrow function: () => addHTTPPermission(...)
250
368
  if (parent && ts.isArrowFunction(parent)) {
251
- // Check if arrow function has no parameters
252
369
  if (parent.parameters.length === 0) {
253
370
  isFactory = true;
254
- // For factories, we need to check the arrow function's parent for the export name
255
- // const apiRoutePermissions = () => addHTTPPermission(...)
256
371
  const arrowParent = parent.parent;
257
372
  if (arrowParent && ts.isVariableDeclaration(arrowParent)) {
258
373
  if (ts.isIdentifier(arrowParent.name)) {
259
- // Check if it's exported
260
374
  if (isNamedExport(arrowParent)) {
261
375
  exportedName = arrowParent.name.text;
262
376
  }
@@ -264,17 +378,14 @@ export const addPermission = (logger, node, checker, state) => {
264
378
  }
265
379
  }
266
380
  }
267
- // If not a factory, get export name from the call expression itself
268
381
  if (!isFactory) {
269
382
  const extracted = extractFunctionName(node, checker, state.rootDir);
270
383
  exportedName = extracted.exportedName;
271
384
  }
272
- // Log warning if not using factory pattern
273
385
  if (!isFactory && exportedName) {
274
386
  logger.warn(`• HTTP permission group '${exportedName}' for pattern '${pattern}' is not wrapped in a factory function. ` +
275
387
  `For tree-shaking, use: export const ${exportedName} = () => addHTTPPermission('${pattern}', [...])`);
276
388
  }
277
- // Store group metadata
278
389
  state.http.routePermissions.set(pattern, {
279
390
  exportName: exportedName,
280
391
  sourceFile: node.getSourceFile().fileName,
@@ -283,7 +394,8 @@ export const addPermission = (logger, node, checker, state) => {
283
394
  optimized: false,
284
395
  services: Array.from(allServices),
285
396
  },
286
- permissionCount: permissionNames.length,
397
+ count: permissionNames.length,
398
+ instanceIds: permissionNames,
287
399
  isFactory,
288
400
  });
289
401
  logger.debug(`• Found HTTP route permission group: ${pattern} -> [${permissionNames.join(', ')}] (${isFactory ? 'factory' : 'direct'})`);
@@ -1,6 +1,6 @@
1
1
  import * as ts from 'typescript';
2
2
  import { getPropertyValue, getCommonWireMetaData, } from '../utils/get-property-value.js';
3
- import { extractFunctionName } from '../utils/extract-function-name.js';
3
+ import { extractFunctionName, makeContextBasedId, } from '../utils/extract-function-name.js';
4
4
  import { getPropertyAssignmentInitializer } from '../utils/type-utils.js';
5
5
  import { resolveMiddleware } from '../utils/middleware.js';
6
6
  import { extractWireNames } from '../utils/post-process.js';
@@ -21,28 +21,34 @@ export const addQueueWorker = (logger, node, checker, state) => {
21
21
  }
22
22
  if (ts.isObjectLiteralExpression(firstArg)) {
23
23
  const obj = firstArg;
24
- const queueName = getPropertyValue(obj, 'queueName');
25
- const { tags, summary, description, errors } = getCommonWireMetaData(obj, 'Queue worker', queueName, logger);
24
+ const name = getPropertyValue(obj, 'name');
25
+ const { disabled, tags, summary, description, errors } = getCommonWireMetaData(obj, 'Queue worker', name, logger);
26
+ if (disabled)
27
+ return;
26
28
  // --- find the referenced function ---
27
29
  const funcInitializer = getPropertyAssignmentInitializer(obj, 'func', true, checker);
28
30
  if (!funcInitializer) {
29
- logger.critical(ErrorCode.MISSING_FUNC, `No valid 'func' property for queue processor '${queueName}'.`);
31
+ logger.critical(ErrorCode.MISSING_FUNC, `No valid 'func' property for queue processor '${name}'.`);
30
32
  return;
31
33
  }
32
- const pikkuFuncName = extractFunctionName(funcInitializer, checker, state.rootDir).pikkuFuncName;
33
- if (!queueName) {
34
- logger.critical(ErrorCode.MISSING_QUEUE_NAME, `No 'queueName' provided for queue processor function '${pikkuFuncName}'.`);
34
+ const extracted = extractFunctionName(funcInitializer, checker, state.rootDir);
35
+ let pikkuFuncId = extracted.pikkuFuncId;
36
+ if (pikkuFuncId.startsWith('__temp_') && name) {
37
+ pikkuFuncId = makeContextBasedId('queue', name);
38
+ }
39
+ if (!name) {
40
+ logger.critical(ErrorCode.MISSING_QUEUE_NAME, `No 'name' provided for queue processor function '${pikkuFuncId}'.`);
35
41
  return;
36
42
  }
37
43
  // --- resolve middleware ---
38
44
  const middleware = resolveMiddleware(state, obj, tags, checker);
39
45
  // --- track used functions/middleware for service aggregation ---
40
- state.serviceAggregation.usedFunctions.add(pikkuFuncName);
41
- extractWireNames(middleware).forEach((name) => state.serviceAggregation.usedMiddleware.add(name));
46
+ state.serviceAggregation.usedFunctions.add(pikkuFuncId);
47
+ extractWireNames(middleware).forEach((n) => state.serviceAggregation.usedMiddleware.add(n));
42
48
  state.queueWorkers.files.add(node.getSourceFile().fileName);
43
- state.queueWorkers.meta[queueName] = {
44
- pikkuFuncName,
45
- queueName,
49
+ state.queueWorkers.meta[name] = {
50
+ pikkuFuncId,
51
+ name,
46
52
  summary,
47
53
  description,
48
54
  errors,
@@ -33,6 +33,20 @@ export function addRPCInvocations(node, state, logger) {
33
33
  }
34
34
  }
35
35
  }
36
+ // Check for workflow('...'), workflowStart('...'), workflowRun('...'), workflowStatus('...'), graphStart('...') calls
37
+ if (ts.isIdentifier(expression) &&
38
+ (expression.text === 'workflow' ||
39
+ expression.text === 'workflowStart' ||
40
+ expression.text === 'workflowRun' ||
41
+ expression.text === 'workflowStatus' ||
42
+ expression.text === 'graphStart')) {
43
+ const [firstArg] = args;
44
+ if (firstArg && ts.isStringLiteral(firstArg)) {
45
+ const workflowName = firstArg.text;
46
+ logger.debug(`• Found ${expression.text}() call: ${workflowName}`);
47
+ state.workflows.invokedWorkflows.add(workflowName);
48
+ }
49
+ }
36
50
  // Check for rpc.invoke('...') calls
37
51
  if (ts.isPropertyAccessExpression(expression) &&
38
52
  expression.name.text === 'invoke' &&
@@ -1,6 +1,6 @@
1
1
  import * as ts from 'typescript';
2
2
  import { getPropertyValue, getCommonWireMetaData, } from '../utils/get-property-value.js';
3
- import { extractFunctionName } from '../utils/extract-function-name.js';
3
+ import { extractFunctionName, makeContextBasedId, } from '../utils/extract-function-name.js';
4
4
  import { getPropertyAssignmentInitializer } from '../utils/type-utils.js';
5
5
  import { resolveMiddleware } from '../utils/middleware.js';
6
6
  import { extractWireNames } from '../utils/post-process.js';
@@ -23,24 +23,30 @@ export const addSchedule = (logger, node, checker, state, options) => {
23
23
  const obj = firstArg;
24
24
  const nameValue = getPropertyValue(obj, 'name');
25
25
  const scheduleValue = getPropertyValue(obj, 'schedule');
26
- const { tags, summary, description, errors } = getCommonWireMetaData(obj, 'Scheduler', nameValue, logger);
26
+ const { disabled, tags, summary, description, errors } = getCommonWireMetaData(obj, 'Scheduler', nameValue, logger);
27
+ if (disabled)
28
+ return;
27
29
  const funcInitializer = getPropertyAssignmentInitializer(obj, 'func', true, checker);
28
30
  if (!funcInitializer) {
29
31
  logger.critical(ErrorCode.MISSING_FUNC, `No valid 'func' property for scheduled task '${nameValue}'.`);
30
32
  return;
31
33
  }
32
- const pikkuFuncName = extractFunctionName(funcInitializer, checker, state.rootDir).pikkuFuncName;
34
+ const extracted = extractFunctionName(funcInitializer, checker, state.rootDir);
35
+ let pikkuFuncId = extracted.pikkuFuncId;
36
+ if (pikkuFuncId.startsWith('__temp_') && nameValue) {
37
+ pikkuFuncId = makeContextBasedId('scheduler', nameValue);
38
+ }
33
39
  if (!nameValue || !scheduleValue) {
34
40
  return;
35
41
  }
36
42
  // --- resolve middleware ---
37
43
  const middleware = resolveMiddleware(state, obj, tags, checker);
38
44
  // --- track used functions/middleware for service aggregation ---
39
- state.serviceAggregation.usedFunctions.add(pikkuFuncName);
45
+ state.serviceAggregation.usedFunctions.add(pikkuFuncId);
40
46
  extractWireNames(middleware).forEach((name) => state.serviceAggregation.usedMiddleware.add(name));
41
47
  state.scheduledTasks.files.add(node.getSourceFile().fileName);
42
48
  state.scheduledTasks.meta[nameValue] = {
43
- pikkuFuncName,
49
+ pikkuFuncId,
44
50
  name: nameValue,
45
51
  schedule: scheduleValue,
46
52
  summary,
@@ -0,0 +1,3 @@
1
+ import { AddWiring } from '../types.js';
2
+ export declare const addSecret: AddWiring;
3
+ export declare const addOAuth2Credential: AddWiring;
@@ -0,0 +1,82 @@
1
+ import * as ts from 'typescript';
2
+ import { getPropertyValue, getArrayPropertyValue, } from '../utils/get-property-value.js';
3
+ import { ErrorCode } from '../error-codes.js';
4
+ import { createAddKeyedWiring } from './add-keyed-wiring.js';
5
+ export const addSecret = createAddKeyedWiring({
6
+ functionName: 'wireSecret',
7
+ idField: 'secretId',
8
+ label: 'Secret',
9
+ schemaPrefix: 'SecretSchema',
10
+ getState: (state) => state.secrets,
11
+ });
12
+ export const addOAuth2Credential = (logger, node, _checker, state, _options) => {
13
+ if (!ts.isCallExpression(node)) {
14
+ return;
15
+ }
16
+ const args = node.arguments;
17
+ const firstArg = args[0];
18
+ const expression = node.expression;
19
+ if (!ts.isIdentifier(expression) ||
20
+ expression.text !== 'wireOAuth2Credential') {
21
+ return;
22
+ }
23
+ if (!firstArg) {
24
+ return;
25
+ }
26
+ if (ts.isObjectLiteralExpression(firstArg)) {
27
+ const obj = firstArg;
28
+ const nameValue = getPropertyValue(obj, 'name');
29
+ const displayNameValue = getPropertyValue(obj, 'displayName');
30
+ const descriptionValue = getPropertyValue(obj, 'description');
31
+ const secretIdValue = getPropertyValue(obj, 'secretId');
32
+ const tokenSecretIdValue = getPropertyValue(obj, 'tokenSecretId');
33
+ const authorizationUrlValue = getPropertyValue(obj, 'authorizationUrl');
34
+ const tokenUrlValue = getPropertyValue(obj, 'tokenUrl');
35
+ const scopesValue = getArrayPropertyValue(obj, 'scopes');
36
+ const pkceValue = getPropertyValue(obj, 'pkce');
37
+ if (!nameValue) {
38
+ logger.critical(ErrorCode.MISSING_NAME, "OAuth2 Credential is missing the required 'name' property.");
39
+ return;
40
+ }
41
+ if (!displayNameValue) {
42
+ logger.critical(ErrorCode.MISSING_NAME, `OAuth2 Credential '${nameValue}' is missing the required 'displayName' property.`);
43
+ return;
44
+ }
45
+ if (!secretIdValue) {
46
+ logger.critical(ErrorCode.MISSING_NAME, `OAuth2 Credential '${nameValue}' is missing the required 'secretId' property.`);
47
+ return;
48
+ }
49
+ if (!tokenSecretIdValue) {
50
+ logger.critical(ErrorCode.MISSING_NAME, `OAuth2 Credential '${nameValue}' is missing the required 'tokenSecretId' property.`);
51
+ return;
52
+ }
53
+ if (!authorizationUrlValue) {
54
+ logger.critical(ErrorCode.MISSING_NAME, `OAuth2 Credential '${nameValue}' is missing the required 'authorizationUrl' property.`);
55
+ return;
56
+ }
57
+ if (!tokenUrlValue) {
58
+ logger.critical(ErrorCode.MISSING_NAME, `OAuth2 Credential '${nameValue}' is missing the required 'tokenUrl' property.`);
59
+ return;
60
+ }
61
+ if (!scopesValue || scopesValue.length === 0) {
62
+ logger.critical(ErrorCode.MISSING_NAME, `OAuth2 Credential '${nameValue}' is missing the required 'scopes' property.`);
63
+ return;
64
+ }
65
+ const sourceFile = node.getSourceFile().fileName;
66
+ state.secrets.files.add(sourceFile);
67
+ state.secrets.definitions.push({
68
+ name: nameValue,
69
+ displayName: displayNameValue,
70
+ description: descriptionValue || undefined,
71
+ secretId: secretIdValue,
72
+ oauth2: {
73
+ tokenSecretId: tokenSecretIdValue,
74
+ authorizationUrl: authorizationUrlValue,
75
+ tokenUrl: tokenUrlValue,
76
+ scopes: scopesValue,
77
+ pkce: pkceValue || undefined,
78
+ },
79
+ sourceFile,
80
+ });
81
+ }
82
+ };
@@ -0,0 +1,2 @@
1
+ import { AddWiring } from '../types.js';
2
+ export declare const addTrigger: AddWiring;
@@ -0,0 +1,87 @@
1
+ import * as ts from 'typescript';
2
+ import { getPropertyValue, getCommonWireMetaData, } from '../utils/get-property-value.js';
3
+ import { extractFunctionName, makeContextBasedId, } from '../utils/extract-function-name.js';
4
+ import { getPropertyAssignmentInitializer } from '../utils/type-utils.js';
5
+ import { resolveMiddleware } from '../utils/middleware.js';
6
+ import { extractWireNames } from '../utils/post-process.js';
7
+ import { resolveExternalPackageName } from '../utils/resolve-external-package.js';
8
+ import { ErrorCode } from '../error-codes.js';
9
+ export const addTrigger = (logger, node, checker, state, options) => {
10
+ if (!ts.isCallExpression(node)) {
11
+ return;
12
+ }
13
+ const args = node.arguments;
14
+ const firstArg = args[0];
15
+ const expression = node.expression;
16
+ if (!ts.isIdentifier(expression)) {
17
+ return;
18
+ }
19
+ if (expression.text === 'wireTrigger') {
20
+ addWireTrigger(logger, node, checker, state, firstArg);
21
+ }
22
+ else if (expression.text === 'wireTriggerSource') {
23
+ addWireTriggerSource(logger, node, checker, state, options, firstArg);
24
+ }
25
+ };
26
+ const addWireTrigger = (logger, node, checker, state, firstArg) => {
27
+ if (!firstArg || !ts.isObjectLiteralExpression(firstArg)) {
28
+ return;
29
+ }
30
+ const obj = firstArg;
31
+ const nameValue = getPropertyValue(obj, 'name');
32
+ const { disabled, tags, summary, description, errors } = getCommonWireMetaData(obj, 'Trigger', nameValue, logger);
33
+ if (disabled)
34
+ return;
35
+ const funcInitializer = getPropertyAssignmentInitializer(obj, 'func', true, checker);
36
+ if (!funcInitializer) {
37
+ logger.critical(ErrorCode.MISSING_FUNC, `No valid 'func' property for trigger '${nameValue}'.`);
38
+ return;
39
+ }
40
+ const extracted = extractFunctionName(funcInitializer, checker, state.rootDir);
41
+ let pikkuFuncId = extracted.pikkuFuncId;
42
+ if (pikkuFuncId.startsWith('__temp_') && nameValue) {
43
+ pikkuFuncId = makeContextBasedId('trigger', nameValue);
44
+ }
45
+ if (!nameValue) {
46
+ return;
47
+ }
48
+ // --- resolve middleware ---
49
+ const middleware = resolveMiddleware(state, obj, tags, checker);
50
+ // --- track used functions/middleware for service aggregation ---
51
+ state.serviceAggregation.usedFunctions.add(pikkuFuncId);
52
+ extractWireNames(middleware).forEach((name) => state.serviceAggregation.usedMiddleware.add(name));
53
+ state.triggers.files.add(node.getSourceFile().fileName);
54
+ state.triggers.meta[nameValue] = {
55
+ pikkuFuncId,
56
+ name: nameValue,
57
+ summary,
58
+ description,
59
+ errors,
60
+ tags,
61
+ middleware,
62
+ };
63
+ };
64
+ const addWireTriggerSource = (logger, node, checker, state, options, firstArg) => {
65
+ if (!firstArg || !ts.isObjectLiteralExpression(firstArg)) {
66
+ return;
67
+ }
68
+ const obj = firstArg;
69
+ const nameValue = getPropertyValue(obj, 'name');
70
+ if (!nameValue) {
71
+ return;
72
+ }
73
+ const funcInitializer = getPropertyAssignmentInitializer(obj, 'func', true, checker);
74
+ if (!funcInitializer) {
75
+ logger.critical(ErrorCode.MISSING_FUNC, `No valid 'func' property for trigger source '${nameValue}'.`);
76
+ return;
77
+ }
78
+ if (ts.isIdentifier(funcInitializer)) {
79
+ const packageName = resolveExternalPackageName(funcInitializer, checker, options.externalPackages);
80
+ state.triggers.sourceMeta[nameValue] = {
81
+ name: nameValue,
82
+ pikkuFuncId: funcInitializer.text,
83
+ packageName: packageName || undefined,
84
+ };
85
+ }
86
+ state.triggers.files.add(node.getSourceFile().fileName);
87
+ };
@@ -0,0 +1 @@
1
+ export declare const addVariable: import("../types.js").AddWiring;