@pikku/inspector 0.12.60 → 0.12.62

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 (139) hide show
  1. package/CHANGELOG.md +322 -0
  2. package/OPTIMIZATION-PLAN.md +19 -19
  3. package/dist/add/add-auth.js +1 -1
  4. package/dist/add/add-channel.d.ts +1 -1
  5. package/dist/add/add-cli.js +5 -0
  6. package/dist/add/add-functions.js +10 -1
  7. package/dist/add/add-http-route.js +18 -18
  8. package/dist/add/add-keyed-wiring.d.ts +2 -0
  9. package/dist/add/add-keyed-wiring.js +3 -1
  10. package/dist/add/add-personas.js +4 -1
  11. package/dist/add/add-secret.js +1 -0
  12. package/dist/add/add-variable.js +1 -0
  13. package/dist/add/add-wire-remote-addon.js +2 -1
  14. package/dist/add/add-workflow-graph.js +63 -6
  15. package/dist/add/add-workflow.d.ts +1 -1
  16. package/dist/error-codes.d.ts +3 -1
  17. package/dist/error-codes.js +3 -1
  18. package/dist/index.d.ts +4 -2
  19. package/dist/index.js +1 -0
  20. package/dist/inspector.js +12 -2
  21. package/dist/types.d.ts +25 -21
  22. package/dist/utils/annotate-http-route-auth.js +2 -1
  23. package/dist/utils/check-secret-sinks.js +2 -1
  24. package/dist/utils/compute-required-schemas.d.ts +1 -1
  25. package/dist/utils/contract-hashes.d.ts +2 -2
  26. package/dist/utils/contract-hashes.js +1 -1
  27. package/dist/utils/extract-function-name.js +1 -1
  28. package/dist/utils/extract-services.d.ts +1 -1
  29. package/dist/utils/extract-surface-usage.d.ts +29 -0
  30. package/dist/utils/extract-surface-usage.js +97 -0
  31. package/dist/utils/filter-inspector-state.js +53 -16
  32. package/dist/utils/filter-utils.d.ts +1 -1
  33. package/dist/utils/get-property-value.js +42 -3
  34. package/dist/utils/load-addon-functions-meta.js +31 -0
  35. package/dist/utils/middleware.d.ts +1 -1
  36. package/dist/utils/permissions.d.ts +1 -1
  37. package/dist/utils/post-process.d.ts +27 -7
  38. package/dist/utils/post-process.js +79 -14
  39. package/dist/utils/resolve-deploy-target.d.ts +1 -1
  40. package/dist/utils/resolve-function-meta.d.ts +1 -1
  41. package/dist/utils/resolve-function-types.d.ts +1 -1
  42. package/dist/utils/resolve-versions.js +1 -1
  43. package/dist/utils/schema-generator.d.ts +1 -1
  44. package/dist/utils/serialize-inspector-state.d.ts +2 -1
  45. package/dist/utils/serialize-inspector-state.js +2 -0
  46. package/dist/utils/serialize-openapi-json.d.ts +2 -2
  47. package/dist/utils/validate-auth-sessionless.js +2 -1
  48. package/dist/utils/workflow/derive-workflow-plan.d.ts +1 -1
  49. package/dist/utils/workflow/dsl/extract-dsl-workflow.d.ts +1 -1
  50. package/dist/utils/workflow/graph/convert-dsl-to-graph.d.ts +1 -1
  51. package/dist/utils/workflow/graph/finalize-workflows.js +1 -1
  52. package/dist/utils/workflow/graph/index.d.ts +2 -1
  53. package/dist/utils/workflow/graph/index.js +1 -1
  54. package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +6 -1
  55. package/dist/utils/workflow/graph/serialize-workflow-graph.js +26 -2
  56. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +11 -2
  57. package/package.json +10 -10
  58. package/run-tests.sh +0 -0
  59. package/src/add/add-agent.ts +2 -4
  60. package/src/add/add-auth.ts +2 -2
  61. package/src/add/add-channel.ts +1 -4
  62. package/src/add/add-cli.ts +7 -1
  63. package/src/add/add-credential.ts +3 -6
  64. package/src/add/add-file-with-factory.ts +2 -6
  65. package/src/add/add-functions.ts +24 -31
  66. package/src/add/add-gateway.ts +1 -1
  67. package/src/add/add-http-route-ref.test.ts +149 -0
  68. package/src/add/add-http-route.ts +26 -29
  69. package/src/add/add-keyed-wiring.ts +8 -7
  70. package/src/add/add-permission.ts +1 -1
  71. package/src/add/add-personas.test.ts +26 -2
  72. package/src/add/add-personas.ts +4 -3
  73. package/src/add/add-scenario-step.test.ts +37 -2
  74. package/src/add/add-scope.test.ts +15 -15
  75. package/src/add/add-scope.ts +5 -9
  76. package/src/add/add-secret.ts +1 -0
  77. package/src/add/add-system-role.test.ts +4 -4
  78. package/src/add/add-system-role.ts +2 -4
  79. package/src/add/add-variable.ts +1 -0
  80. package/src/add/add-wire-remote-addon.ts +4 -1
  81. package/src/add/add-workflow-graph-foreach.test.ts +174 -0
  82. package/src/add/add-workflow-graph-input.test.ts +1 -1
  83. package/src/add/add-workflow-graph.ts +78 -6
  84. package/src/add/add-workflow-sleep-expression.test.ts +3 -4
  85. package/src/add/add-workflow.ts +1 -4
  86. package/src/add/addon-contracts.test.ts +1 -1
  87. package/src/add/config-value-metadata.test.ts +94 -0
  88. package/src/add/secret-sink-check.test.ts +4 -1
  89. package/src/error-codes.ts +3 -1
  90. package/src/index.ts +19 -5
  91. package/src/inspector.ts +19 -2
  92. package/src/types.ts +25 -28
  93. package/src/utils/annotate-http-route-auth.test.ts +5 -7
  94. package/src/utils/annotate-http-route-auth.ts +2 -1
  95. package/src/utils/check-secret-sinks.ts +4 -1
  96. package/src/utils/compute-required-schemas.ts +1 -1
  97. package/src/utils/contract-hashes.test.ts +1 -1
  98. package/src/utils/contract-hashes.ts +3 -3
  99. package/src/utils/extract-function-name.ts +1 -1
  100. package/src/utils/extract-services.ts +1 -2
  101. package/src/utils/extract-surface-usage.test.ts +147 -0
  102. package/src/utils/extract-surface-usage.ts +121 -0
  103. package/src/utils/filter-inspector-state.test.ts +138 -12
  104. package/src/utils/filter-inspector-state.ts +82 -21
  105. package/src/utils/filter-utils.ts +1 -1
  106. package/src/utils/get-property-value.ts +43 -5
  107. package/src/utils/load-addon-functions-meta.ts +40 -0
  108. package/src/utils/middleware.ts +1 -1
  109. package/src/utils/permissions.ts +1 -1
  110. package/src/utils/post-process.test.ts +59 -2
  111. package/src/utils/post-process.ts +88 -25
  112. package/src/utils/resolve-deploy-target.ts +1 -1
  113. package/src/utils/resolve-function-meta.ts +1 -4
  114. package/src/utils/resolve-function-types.ts +1 -1
  115. package/src/utils/resolve-versions.ts +2 -5
  116. package/src/utils/schema-generator.ts +3 -3
  117. package/src/utils/serialize-inspector-state.ts +4 -1
  118. package/src/utils/serialize-mcp-json.ts +1 -1
  119. package/src/utils/serialize-openapi-json.ts +2 -2
  120. package/src/utils/validate-auth-sessionless.ts +2 -1
  121. package/src/utils/validate-tags-resolve-to-middleware.test.ts +4 -1
  122. package/src/utils/workflow/derive-workflow-plan.test.ts +1 -1
  123. package/src/utils/workflow/derive-workflow-plan.ts +1 -1
  124. package/src/utils/workflow/dsl/extract-approval-policy.test.ts +1 -1
  125. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +2 -2
  126. package/src/utils/workflow/graph/convert-dsl-to-graph-skip.test.ts +1 -1
  127. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +1 -4
  128. package/src/utils/workflow/graph/finalize-workflow-wires.ts +1 -1
  129. package/src/utils/workflow/graph/finalize-workflows.ts +2 -2
  130. package/src/utils/workflow/graph/index.ts +18 -1
  131. package/src/utils/workflow/graph/serialize-workflow-graph.ts +34 -3
  132. package/src/utils/workflow/graph/workflow-graph.types.ts +12 -2
  133. package/tsconfig.tsbuildinfo +1 -1
  134. package/dist/add/add-ai-agent.d.ts +0 -4
  135. package/dist/add/add-ai-agent.js +0 -442
  136. package/dist/add/add-ai-scorer.d.ts +0 -2
  137. package/dist/add/add-ai-scorer.js +0 -61
  138. package/dist/utils/secret-brokers.d.ts +0 -2
  139. package/dist/utils/secret-brokers.js +0 -9
@@ -10,7 +10,8 @@ import type {
10
10
  function extractAstValue(
11
11
  expr: ts.Expression,
12
12
  refParamName: string,
13
- templateParamName: string | undefined
13
+ templateParamName: string | undefined,
14
+ itemParamName?: string
14
15
  ): unknown {
15
16
  // `'GET' as const` / parenthesised values wrap the real initializer — see
16
17
  // through them or the whole property is silently dropped from the meta.
@@ -43,6 +44,12 @@ function extractAstValue(
43
44
  args[1] && ts.isStringLiteral(args[1]) ? args[1].text : undefined
44
45
  return { $ref: nodeId, path } as DataRef
45
46
  }
47
+ if (itemParamName && callee.text === itemParamName) {
48
+ const pathArg = expr.arguments[0]
49
+ const path =
50
+ pathArg && ts.isStringLiteral(pathArg) ? pathArg.text : undefined
51
+ return { $ref: '$item', path } as DataRef
52
+ }
46
53
  if (templateParamName && callee.text === templateParamName) {
47
54
  const templateStr =
48
55
  expr.arguments[0] && ts.isStringLiteral(expr.arguments[0])
@@ -55,7 +62,8 @@ function extractAstValue(
55
62
  const resolved = extractAstValue(
56
63
  el,
57
64
  refParamName,
58
- templateParamName
65
+ templateParamName,
66
+ itemParamName
59
67
  )
60
68
  if (
61
69
  typeof resolved === 'object' &&
@@ -84,7 +92,7 @@ function extractAstValue(
84
92
  }
85
93
  if (ts.isArrayLiteralExpression(expr)) {
86
94
  return expr.elements.map((el) =>
87
- extractAstValue(el, refParamName, templateParamName)
95
+ extractAstValue(el, refParamName, templateParamName, itemParamName)
88
96
  )
89
97
  }
90
98
  if (ts.isObjectLiteralExpression(expr)) {
@@ -100,7 +108,8 @@ function extractAstValue(
100
108
  obj[key] = extractAstValue(
101
109
  prop.initializer,
102
110
  refParamName,
103
- templateParamName
111
+ templateParamName,
112
+ itemParamName
104
113
  )
105
114
  }
106
115
  return obj
@@ -160,6 +169,13 @@ function extractInputMapping(
160
169
  ? node.parameters[1].name.text
161
170
  : 'template'
162
171
 
172
+ // `$item` is appended after `template` so neither existing arrow shape —
173
+ // `(ref) => ...` nor `(ref, template) => ...` — shifts a parameter.
174
+ const itemParamName =
175
+ node.parameters.length > 2 && ts.isIdentifier(node.parameters[2].name)
176
+ ? node.parameters[2].name.text
177
+ : undefined
178
+
163
179
  const input: Record<string, unknown | DataRef> = {}
164
180
 
165
181
  for (const prop of bodyObj.properties) {
@@ -176,7 +192,8 @@ function extractInputMapping(
176
192
  const value = extractAstValue(
177
193
  prop.initializer,
178
194
  refParamName,
179
- templateParamName
195
+ templateParamName,
196
+ itemParamName
180
197
  )
181
198
  if (value !== undefined) {
182
199
  input[key] = value
@@ -186,6 +203,49 @@ function extractInputMapping(
186
203
  return input
187
204
  }
188
205
 
206
+ /**
207
+ * Extract a `forEach` source, authored either as a bare node id (`'listRows'`)
208
+ * or as a callback returning a ref (`(ref) => ref('listRows', 'rows')`).
209
+ */
210
+ function extractForEachConfig(node: ts.Node): DataRef | undefined {
211
+ while (ts.isAsExpression(node) || ts.isParenthesizedExpression(node)) {
212
+ node = node.expression
213
+ }
214
+
215
+ if (ts.isStringLiteral(node)) {
216
+ return { $ref: node.text }
217
+ }
218
+
219
+ if (ts.isArrowFunction(node)) {
220
+ const refParamName =
221
+ node.parameters.length > 0 && ts.isIdentifier(node.parameters[0].name)
222
+ ? node.parameters[0].name.text
223
+ : 'ref'
224
+
225
+ let body: ts.Node = node.body
226
+ if (ts.isBlock(body)) {
227
+ for (const stmt of body.statements) {
228
+ if (ts.isReturnStatement(stmt) && stmt.expression) {
229
+ body = stmt.expression
230
+ }
231
+ }
232
+ }
233
+ if (!ts.isExpression(body)) return undefined
234
+
235
+ const value = extractAstValue(body, refParamName, undefined)
236
+ if (
237
+ typeof value === 'object' &&
238
+ value !== null &&
239
+ '$ref' in value &&
240
+ typeof (value as DataRef).$ref === 'string'
241
+ ) {
242
+ return value as DataRef
243
+ }
244
+ }
245
+
246
+ return undefined
247
+ }
248
+
189
249
  /**
190
250
  * Extract next config (string, array, or record)
191
251
  */
@@ -380,6 +440,8 @@ function extractGraphFromNewFormat(
380
440
  nodeId,
381
441
  rpcName,
382
442
  input: {},
443
+ forEach: undefined,
444
+ mode: undefined,
383
445
  next: undefined,
384
446
  onError: undefined,
385
447
  retries: undefined,
@@ -410,6 +472,8 @@ function extractGraphFromNewFormat(
410
472
  nodes[nodeId].next = nodeConfig.next
411
473
  nodes[nodeId].onError = nodeConfig.onError
412
474
  nodes[nodeId].input = nodeConfig.input
475
+ nodes[nodeId].forEach = nodeConfig.forEach
476
+ nodes[nodeId].mode = nodeConfig.mode
413
477
  nodes[nodeId].retries = nodeConfig.retries
414
478
  nodes[nodeId].retryDelay = nodeConfig.retryDelay
415
479
  nodes[nodeId].notes = nodeConfig.notes
@@ -431,6 +495,8 @@ function extractNodeConfigFromObject(
431
495
  next: any
432
496
  onError: any
433
497
  input: Record<string, any>
498
+ forEach: DataRef | undefined
499
+ mode: string | undefined
434
500
  retries: number | undefined
435
501
  retryDelay: string | number | undefined
436
502
  notes: string | undefined
@@ -438,6 +504,8 @@ function extractNodeConfigFromObject(
438
504
  let next: any = undefined
439
505
  let onError: any = undefined
440
506
  let input: Record<string, any> = {}
507
+ let forEach: DataRef | undefined = undefined
508
+ let mode: string | undefined = undefined
441
509
  let retries: number | undefined = undefined
442
510
  let retryDelay: string | number | undefined = undefined
443
511
  let notes: string | undefined = undefined
@@ -453,6 +521,10 @@ function extractNodeConfigFromObject(
453
521
  onError = extractNextConfig(prop.initializer, checker)
454
522
  } else if (propName === 'input') {
455
523
  input = extractInputMapping(prop.initializer, checker)
524
+ } else if (propName === 'forEach') {
525
+ forEach = extractForEachConfig(prop.initializer)
526
+ } else if (propName === 'mode') {
527
+ mode = extractStringLiteral(prop.initializer, checker)
456
528
  } else if (propName === 'retries') {
457
529
  if (ts.isNumericLiteral(prop.initializer)) {
458
530
  retries = Number(prop.initializer.text)
@@ -468,7 +540,7 @@ function extractNodeConfigFromObject(
468
540
  }
469
541
  }
470
542
 
471
- return { next, onError, input, retries, retryDelay, notes }
543
+ return { next, onError, input, forEach, mode, retries, retryDelay, notes }
472
544
  }
473
545
 
474
546
  /**
@@ -77,10 +77,9 @@ describe('sleep with a duration that is not a literal', () => {
77
77
 
78
78
  test('a literal duration still round-trips as a value', async () => {
79
79
  const { graph, diags } = await run(
80
- [
81
- " await workflow.sleep('Wait', '5s')",
82
- ' return { sent: true }',
83
- ].join('\n')
80
+ [" await workflow.sleep('Wait', '5s')", ' return { sent: true }'].join(
81
+ '\n'
82
+ )
84
83
  )
85
84
 
86
85
  assert.deepEqual(diags, [])
@@ -3,10 +3,7 @@ import type { AddWiring, InspectorState } from '../types.js'
3
3
  import { extractFunctionName } from '../utils/extract-function-name.js'
4
4
  import { extractFunctionNode } from '../utils/extract-function-node.js'
5
5
  import { ErrorCode } from '../error-codes.js'
6
- import type {
7
- WorkflowStepMeta,
8
- WorkflowContext,
9
- } from '@pikku/core/ecosystem/workflow'
6
+ import type { WorkflowStepMeta, WorkflowContext } from '@pikku/core/workflow'
10
7
  import {
11
8
  extractStringLiteral,
12
9
  isStringLike,
@@ -178,7 +178,7 @@ describe('addon contract metadata', () => {
178
178
  await writeFile(
179
179
  appFile,
180
180
  [
181
- "import { wireAddon } from '@pikku/core/ecosystem/rpc'",
181
+ "import { wireAddon } from '@pikku/core/addon'",
182
182
  "import { wireHTTPRoutes } from '@pikku/core/http'",
183
183
  "import { wireChannel } from '@pikku/core/channel'",
184
184
  "import { wireCLI } from '@pikku/core/cli'",
@@ -167,3 +167,97 @@ describe('config value metadata (docsUrl)', () => {
167
167
  }
168
168
  })
169
169
  })
170
+
171
+ /**
172
+ * `optional` is the deploy gate's whole input. A flag the
173
+ * inspector drops leaves the gate seeing a bare declaration, which is how an
174
+ * optional secret suspends a deploy over a value production is meant NOT to
175
+ * have.
176
+ */
177
+ describe('config value gating flags', () => {
178
+ test('carries optional from defineSecret into the definition', async () => {
179
+ const rootDir = await mkdtemp(`${fixtureRoot}-secret-optional-`)
180
+ const file = join(rootDir, 'secret.ts')
181
+
182
+ await writeFile(
183
+ file,
184
+ [
185
+ "import { defineSecret } from '@pikku/core'",
186
+ "import { z } from 'zod'",
187
+ 'export const ActorSchema = z.string()',
188
+ 'export const StripeSchema = z.string()',
189
+ 'defineSecret({',
190
+ " name: 'scenarioActor',",
191
+ " displayName: 'Scenario Actor Secret',",
192
+ " secretId: 'SCENARIO_ACTOR_SECRET',",
193
+ ' optional: true,',
194
+ ' schema: ActorSchema,',
195
+ '})',
196
+ 'defineSecret({',
197
+ " name: 'stripe',",
198
+ " displayName: 'Stripe',",
199
+ " secretId: 'STRIPE_KEY',",
200
+ ' schema: StripeSchema,',
201
+ '})',
202
+ ].join('\n')
203
+ )
204
+
205
+ const criticals: Array<{ code: ErrorCode; message: string }> = []
206
+ try {
207
+ const state = await inspect(makeLogger(criticals), [file], { rootDir })
208
+ const optional = state.secrets.definitions.find(
209
+ (d) => d.secretId === 'SCENARIO_ACTOR_SECRET'
210
+ )
211
+ const required = state.secrets.definitions.find(
212
+ (d) => d.secretId === 'STRIPE_KEY'
213
+ )
214
+ assert.equal(optional!.optional, true)
215
+ assert.equal(required!.optional, undefined)
216
+ } finally {
217
+ await rm(rootDir, { recursive: true, force: true })
218
+ }
219
+ })
220
+
221
+ test('carries optional from defineVariable into the definition', async () => {
222
+ const rootDir = await mkdtemp(`${fixtureRoot}-variable-optional-`)
223
+ const file = join(rootDir, 'variable.ts')
224
+
225
+ await writeFile(
226
+ file,
227
+ [
228
+ "import { defineVariable } from '@pikku/core'",
229
+ "import { z } from 'zod'",
230
+ 'export const ConsoleUrlSchema = z.string()',
231
+ 'export const CorsSchema = z.string()',
232
+ 'defineVariable({',
233
+ " name: 'consoleUrl',",
234
+ " displayName: 'Console URL',",
235
+ " variableId: 'CONSOLE_URL',",
236
+ ' optional: true,',
237
+ ' schema: ConsoleUrlSchema,',
238
+ '})',
239
+ 'defineVariable({',
240
+ " name: 'corsOrigins',",
241
+ " displayName: 'Allowed Browser Origins',",
242
+ " variableId: 'CORS_ORIGINS',",
243
+ ' schema: CorsSchema,',
244
+ '})',
245
+ ].join('\n')
246
+ )
247
+
248
+ const criticals: Array<{ code: ErrorCode; message: string }> = []
249
+ try {
250
+ const state = await inspect(makeLogger(criticals), [file], { rootDir })
251
+ const optional = state.variables.definitions.find(
252
+ (d) => d.variableId === 'CONSOLE_URL'
253
+ )
254
+ const required = state.variables.definitions.find(
255
+ (d) => d.variableId === 'CORS_ORIGINS'
256
+ )
257
+ assert.equal(optional!.optional, true)
258
+ assert.equal(required!.optional, undefined)
259
+ } finally {
260
+ await rm(rootDir, { recursive: true, force: true })
261
+ }
262
+ })
263
+ })
@@ -75,7 +75,10 @@ async function runInspect(source: string) {
75
75
  await writeFile(file, PRELUDE + source)
76
76
  const { logger, diagnostics } = makeLogger()
77
77
  try {
78
- await inspect(logger, [file], { rootDir: tmpDir, classificationCheck: true })
78
+ await inspect(logger, [file], {
79
+ rootDir: tmpDir,
80
+ classificationCheck: true,
81
+ })
79
82
  } finally {
80
83
  await rm(tmpDir, { recursive: true, force: true })
81
84
  }
@@ -22,7 +22,7 @@ export enum ErrorCode {
22
22
  CLI_CLIENTSIDE_RENDERER_HAS_SERVICES = 'PKU672',
23
23
  SCENARIO_HAS_SERVICES = 'PKU673',
24
24
  EXPECT_EVENTUALLY_SCENARIO_ONLY = 'PKU675',
25
- SCENARIO_BROWSER_STEP_NEEDS_ACTOR = 'PKU677',
25
+ SCENARIO_STEP_NEEDS_ACTOR = 'PKU677',
26
26
  SCENARIO_STEP_TARGET_NOT_STATIC = 'PKU678',
27
27
  SCENARIO_NOT_EXTRACTABLE = 'PKU679',
28
28
  SCENARIO_HAS_NO_ASSERTION = 'PKU680',
@@ -34,6 +34,7 @@ export enum ErrorCode {
34
34
  // Remote addon (wireRemoteAddon) validation
35
35
  REMOTE_ADDON_NOT_DEV_DEPENDENCY = 'PKU338',
36
36
  REMOTE_ADDON_AUTH_UNRESOLVED = 'PKU339',
37
+ ADDON_NOT_INSTALLED = 'PKU340',
37
38
 
38
39
  // Database schema codegen warnings
39
40
  DB_COLUMN_NAME_TYPE_CONTRADICTION = 'PKU480',
@@ -106,6 +107,7 @@ export enum ErrorCode {
106
107
  DUPLICATE_CORE_VERSION = 'PKU717',
107
108
  CORE_VERSION_SKEW = 'PKU718',
108
109
  SPLIT_TYPE_IDENTITY = 'PKU719',
110
+ SINGLETON_SERVICES_TYPE_UNRESOLVED = 'PKU724',
109
111
 
110
112
  // Data classification errors
111
113
  PII_IN_OUTPUT = 'PKU910',
package/src/index.ts CHANGED
@@ -1,6 +1,21 @@
1
1
  export { inspect, getInitialInspectorState } from './inspector.js'
2
2
  export type { TypesMap } from './types-map.js'
3
- export type * from './types.js'
3
+ export type {
4
+ AddonConfig,
5
+ AuthDefinition,
6
+ InspectorDiagnostic,
7
+ InspectorFilters,
8
+ InspectorHTTPState,
9
+ InspectorLogger,
10
+ InspectorMiddlewareState,
11
+ InspectorOptions,
12
+ InspectorPermissionState,
13
+ InspectorState,
14
+ MetaInputTypes,
15
+ MiddlewareGroupMeta,
16
+ MiddlewareRegistration,
17
+ SchemaRef,
18
+ } from './types.js'
4
19
  export type { FilesAndMethodsErrors } from './utils/get-files-and-methods.js'
5
20
  export { ErrorCode } from './error-codes.js'
6
21
  export type { DiagnosticSeverity, CodedDiagnostic } from './error-codes.js'
@@ -21,6 +36,8 @@ export {
21
36
  generateCustomTypes,
22
37
  sanitizeTypeName,
23
38
  } from './utils/custom-types-generator.js'
39
+ export { unresolvedSchemaReferences } from './utils/post-process.js'
40
+ export type { SurfaceUsageCounts } from './utils/extract-surface-usage.js'
24
41
  export {
25
42
  createEmptyManifest,
26
43
  serializeManifest,
@@ -32,10 +49,7 @@ export type {
32
49
  VersionManifest,
33
50
  VersionManifestEntry,
34
51
  } from './utils/contract-hashes.js'
35
- export type {
36
- InspectorFeature,
37
- InspectorFeatureEntry,
38
- } from './types.js'
52
+ export type { InspectorFeature, InspectorFeatureEntry } from './types.js'
39
53
  export { serializeMCPJson } from './utils/serialize-mcp-json.js'
40
54
  export type { OpenAPISpecInfo } from './utils/serialize-openapi-json.js'
41
55
  export {
package/src/inspector.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as ts from 'typescript'
2
2
  import { performance } from 'perf_hooks'
3
- import { dirname, resolve } from 'path'
3
+ import { dirname, relative, resolve } from 'path'
4
4
  import { visitSetup, visitFunctions, visitRoutes } from './visit.js'
5
5
  import { TypesMap } from './types-map.js'
6
6
  import type {
@@ -42,7 +42,7 @@ import { validateExposedFunctionsGated } from './utils/validate-exposed-function
42
42
  import { validateTagsResolveToMiddleware } from './utils/validate-tags-resolve-to-middleware.js'
43
43
  import { annotateHttpRouteAuth } from './utils/annotate-http-route-auth.js'
44
44
  import { generateOpenAPISpec } from './utils/serialize-openapi-json.js'
45
- import { pikkuState } from '@pikku/core/ecosystem'
45
+ import { pikkuState } from '@pikku/core/state'
46
46
  import { resolveLatestVersions } from './utils/resolve-versions.js'
47
47
  import { finalizeWorkflows } from './utils/workflow/graph/finalize-workflows.js'
48
48
  import {
@@ -51,6 +51,10 @@ import {
51
51
  } from './utils/workflow/graph/finalize-workflow-wires.js'
52
52
  import { generateAllSchemas } from './utils/schema-generator.js'
53
53
  import { extractSecretUsage } from './utils/extract-secret-usage.js'
54
+ import {
55
+ accumulateSurfaceUsage,
56
+ surfaceUsageArea,
57
+ } from './utils/extract-surface-usage.js'
54
58
  import {
55
59
  loadAddonFunctionsMeta,
56
60
  loadAddonSchemas,
@@ -186,6 +190,7 @@ export function getInitialInspectorState(rootDir: string): InspectorState {
186
190
  files: new Set(),
187
191
  usage: new Map(),
188
192
  },
193
+ surfaceUsage: {},
189
194
  credentials: {
190
195
  definitions: [],
191
196
  files: new Set(),
@@ -378,11 +383,23 @@ export const inspect = async (
378
383
 
379
384
  const state = getInitialInspectorState(rootDir)
380
385
 
386
+ const surfaceAreaCache = new Map<string, string | null>()
381
387
  for (const sourceFile of sourceFiles) {
382
388
  const usage = extractSecretUsage(sourceFile)
383
389
  if (usage.keys.length > 0 || usage.dynamic.length > 0) {
384
390
  state.secrets.usage.set(sourceFile.fileName, usage)
385
391
  }
392
+ // Counted in the sweep that already has this file open. The public surface
393
+ // is documentation, and documentation may not cost a project a second pass
394
+ // over its source.
395
+ accumulateSurfaceUsage(
396
+ sourceFile,
397
+ {
398
+ area: surfaceUsageArea(sourceFile.fileName, rootDir, surfaceAreaCache),
399
+ file: relative(rootDir, sourceFile.fileName),
400
+ },
401
+ state.surfaceUsage
402
+ )
386
403
  }
387
404
 
388
405
  // First sweep: add all functions
package/src/types.ts CHANGED
@@ -1,41 +1,36 @@
1
1
  import type * as ts from 'typescript'
2
2
  import type { SecretUsage } from './utils/extract-secret-usage.js'
3
- import type {
4
- ChannelMessageMeta,
5
- ChannelsMeta,
6
- } from '@pikku/core/ecosystem/channel'
7
- import type { GatewaysMeta } from '@pikku/core/ecosystem/gateway'
8
- import type { HTTPWiringsMeta } from '@pikku/core/ecosystem/http'
9
- import type { ScheduledTasksMeta } from '@pikku/core/ecosystem/scheduler'
10
- import type {
11
- TriggerMeta,
12
- TriggerSourceMeta,
13
- } from '@pikku/core/ecosystem/trigger'
14
- import type { QueueWorkersMeta } from '@pikku/core/ecosystem/queue'
15
- import type { WorkflowsMeta } from '@pikku/core/ecosystem/workflow'
3
+ import type { SurfaceUsageCounts } from './utils/extract-surface-usage.js'
4
+ import type { ChannelMessageMeta, ChannelsMeta } from '@pikku/core/channel'
5
+ import type { GatewaysMeta } from '@pikku/core/gateway'
6
+ import type { HTTPWiringsMeta } from '@pikku/core/http'
7
+ import type { ScheduledTasksMeta } from '@pikku/core/scheduler'
8
+ import type { TriggerMeta, TriggerSourceMeta } from '@pikku/core/trigger'
9
+ import type { QueueWorkersMeta } from '@pikku/core/queue'
10
+ import type { WorkflowsMeta } from '@pikku/core/workflow'
16
11
  import type {
17
12
  MCPResourceMeta,
18
13
  MCPToolMeta,
19
14
  MCPPromptMeta,
20
- } from '@pikku/core/ecosystem/mcp'
21
- import type { AgentsMeta } from '@pikku/core/ecosystem/agent'
22
- import type { ScorerMeta } from '@pikku/core/ecosystem/agent-scorer'
23
- import type { CLIMeta } from '@pikku/core/ecosystem/cli'
24
- import type { CLICommandMeta } from '@pikku/core/ecosystem/cli'
25
- import type { NodesMeta } from '@pikku/core/ecosystem/node'
26
- import type { SecretDefinitions } from '@pikku/core/ecosystem/secret'
27
- import type { CredentialDefinitions } from '@pikku/core/ecosystem/credential'
28
- import type { ScopeDefinitions } from '@pikku/core/ecosystem/scope'
29
- import type { SystemRoleDefinitions } from '@pikku/core/ecosystem/role'
30
- import type { PersonaDefinitions } from '@pikku/core/ecosystem/persona'
31
- import type { VariableDefinitions } from '@pikku/core/ecosystem/variable'
15
+ } from '@pikku/core/mcp'
16
+ import type { AgentsMeta } from '@pikku/core/agent'
17
+ import type { ScorerMeta } from '@pikku/core/agent-scorer'
18
+ import type { CLIMeta } from '@pikku/core/cli'
19
+ import type { CLICommandMeta } from '@pikku/core/cli'
20
+ import type { NodesMeta } from '@pikku/core/node'
21
+ import type { SecretDefinitions } from '@pikku/core/secret'
22
+ import type { CredentialDefinitions } from '@pikku/core/credential'
23
+ import type { ScopeDefinitions } from '@pikku/core/scope'
24
+ import type { SystemRoleDefinitions } from '@pikku/core/role'
25
+ import type { PersonaDefinitions } from '@pikku/core/persona'
26
+ import type { VariableDefinitions } from '@pikku/core/variable'
32
27
  import type { TypesMap } from './types-map.js'
33
- import type { FunctionsMeta } from '@pikku/core/ecosystem/services'
28
+ import type { FunctionsMeta } from '@pikku/core/services'
34
29
  import type {
35
30
  FunctionServicesMeta,
36
31
  FunctionWiresMeta,
37
- JSONValue,
38
- } from '@pikku/core/ecosystem/types'
32
+ } from '@pikku/core/function'
33
+ import type { JSONValue } from '@pikku/core/utils'
39
34
  import type { OpenAPISpecInfo } from './utils/serialize-openapi-json.js'
40
35
  import type { ErrorCode, CodedDiagnostic } from './error-codes.js'
41
36
  import type {
@@ -660,6 +655,8 @@ export interface InspectorState {
660
655
  /** Secret reads found per source file, keyed by absolute path. */
661
656
  usage: Map<string, SecretUsage>
662
657
  }
658
+ /** How often each `#pikku/*` export is imported, and from where. */
659
+ surfaceUsage: SurfaceUsageCounts
663
660
  credentials: {
664
661
  definitions: CredentialDefinitions
665
662
  files: Set<string>
@@ -108,18 +108,16 @@ describe('annotateHttpRouteAuth', () => {
108
108
 
109
109
  test('a ref route resolves against its target', () => {
110
110
  const state = {
111
- functions: {
112
- meta: {
113
- inline: { pikkuFuncId: 'inline', sessionless: true },
114
- target: { pikkuFuncId: 'target', sessionless: false },
115
- },
111
+ functions: { meta: {} },
112
+ addonFunctions: {
113
+ console: { target: { pikkuFuncId: 'target', sessionless: false } },
116
114
  },
117
115
  http: {
118
116
  meta: {
119
117
  get: {
120
118
  '/thing': {
121
- pikkuFuncId: 'inline',
122
- refTarget: 'target',
119
+ pikkuFuncId: 'console:target',
120
+ packageName: ADDON_PACKAGE,
123
121
  route: '/thing',
124
122
  method: 'get',
125
123
  },
@@ -1,4 +1,5 @@
1
1
  import type { InspectorState } from '../types.js'
2
+ import { resolveFunctionMeta } from './resolve-function-meta.js'
2
3
 
3
4
  /**
4
5
  * Resolve, per HTTP route, whether reaching it requires a session.
@@ -25,7 +26,7 @@ export function annotateHttpRouteAuth(state: InspectorState): void {
25
26
 
26
27
  for (const routes of Object.values(state.http?.meta ?? {})) {
27
28
  for (const route of Object.values(routes)) {
28
- const meta = state.functions.meta[route.refTarget ?? route.pikkuFuncId]
29
+ const meta = resolveFunctionMeta(state, route.pikkuFuncId)
29
30
 
30
31
  route.requiresSession =
31
32
  // A pikkuFunc always requires a session, on every path.
@@ -53,7 +53,10 @@ export function findRevealedSecretSinks(
53
53
  const violations: SecretSinkViolation[] = []
54
54
 
55
55
  const visit = (node: ts.Node): void => {
56
- if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
56
+ if (
57
+ ts.isCallExpression(node) &&
58
+ ts.isPropertyAccessExpression(node.expression)
59
+ ) {
57
60
  const sink = describeSink(checker, node.expression)
58
61
  if (sink) {
59
62
  for (const arg of node.arguments) {
@@ -1,4 +1,4 @@
1
- import type { FunctionsMeta } from '@pikku/core/ecosystem/services'
1
+ import type { FunctionsMeta } from '@pikku/core/services'
2
2
  import type { TypesMap } from '../types-map.js'
3
3
  import type { SchemaRef } from '../types.js'
4
4
 
@@ -10,7 +10,7 @@ import {
10
10
  extractContractsFromMeta,
11
11
  } from './contract-hashes.js'
12
12
  import type { ContractEntry, VersionManifest } from './contract-hashes.js'
13
- import type { FunctionsMeta } from '@pikku/core/ecosystem/services'
13
+ import type { FunctionsMeta } from '@pikku/core/services'
14
14
  import { ErrorCode } from '../error-codes.js'
15
15
 
16
16
  function makeContracts(
@@ -1,6 +1,6 @@
1
- import type { FunctionsMeta } from '@pikku/core/ecosystem/services'
2
- import type { JSONValue } from '@pikku/core/ecosystem/types'
3
- import { parseVersionedId } from '@pikku/core/ecosystem/types'
1
+ import type { FunctionsMeta } from '@pikku/core/services'
2
+ import type { JSONValue } from '@pikku/core/utils'
3
+ import { parseVersionedId } from '@pikku/core/utils'
4
4
  import type { TypesMap } from '../types-map.js'
5
5
  import { ErrorCode } from '../error-codes.js'
6
6
  import { canonicalJSON, hashString } from './hash.js'
@@ -1,7 +1,7 @@
1
1
  import * as ts from 'typescript'
2
2
  import { createHash } from 'crypto'
3
3
  import { relative } from 'path'
4
- import { formatVersionedId } from '@pikku/core/ecosystem/types'
4
+ import { formatVersionedId } from '@pikku/core/utils'
5
5
 
6
6
  /**
7
7
  * Deterministic placeholder id for an anonymous/unnamed pikku function or
@@ -2,8 +2,7 @@ import * as ts from 'typescript'
2
2
  import type {
3
3
  FunctionServicesMeta,
4
4
  FunctionWiresMeta,
5
- } from '@pikku/core/ecosystem/types'
6
-
5
+ } from '@pikku/core/function'
7
6
  /**
8
7
  * Extract services from a function's first parameter destructuring pattern
9
8
  */