@pikku/inspector 0.12.3 → 0.12.5

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 (38) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/add/add-credential.d.ts +2 -0
  3. package/dist/add/add-credential.js +118 -0
  4. package/dist/add/add-middleware.js +6 -10
  5. package/dist/add/add-permission.js +10 -12
  6. package/dist/add/add-secret.d.ts +1 -3
  7. package/dist/add/add-secret.js +0 -74
  8. package/dist/add/add-workflow.js +7 -1
  9. package/dist/error-codes.d.ts +1 -0
  10. package/dist/error-codes.js +2 -0
  11. package/dist/inspector.js +18 -7
  12. package/dist/types.d.ts +7 -0
  13. package/dist/utils/custom-types-generator.js +1 -0
  14. package/dist/utils/post-process.d.ts +9 -0
  15. package/dist/utils/post-process.js +46 -0
  16. package/dist/utils/schema-generator.js +26 -6
  17. package/dist/utils/serialize-inspector-state.d.ts +4 -0
  18. package/dist/utils/serialize-inspector-state.js +9 -0
  19. package/dist/utils/workflow/graph/convert-dsl-to-graph.js +1 -0
  20. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +2 -0
  21. package/dist/visit.js +3 -2
  22. package/package.json +4 -3
  23. package/src/add/add-credential.ts +178 -0
  24. package/src/add/add-middleware.ts +6 -14
  25. package/src/add/add-permission.ts +10 -16
  26. package/src/add/add-secret.ts +0 -131
  27. package/src/add/add-workflow.ts +11 -1
  28. package/src/error-codes.ts +3 -0
  29. package/src/inspector.ts +25 -6
  30. package/src/types.ts +7 -0
  31. package/src/utils/custom-types-generator.ts +1 -0
  32. package/src/utils/post-process.ts +59 -0
  33. package/src/utils/schema-generator.ts +38 -10
  34. package/src/utils/serialize-inspector-state.ts +13 -0
  35. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +1 -0
  36. package/src/utils/workflow/graph/workflow-graph.types.ts +2 -0
  37. package/src/visit.ts +3 -2
  38. package/tsconfig.tsbuildinfo +1 -1
package/src/types.ts CHANGED
@@ -15,6 +15,7 @@ import type { AIAgentMeta } from '@pikku/core/ai-agent'
15
15
  import type { CLIMeta } from '@pikku/core/cli'
16
16
  import type { NodesMeta } from '@pikku/core/node'
17
17
  import type { SecretDefinitions } from '@pikku/core/secret'
18
+ import type { CredentialDefinitions } from '@pikku/core/credential'
18
19
  import type { VariableDefinitions } from '@pikku/core/variable'
19
20
  import type { TypesMap } from './types-map.js'
20
21
  import type {
@@ -225,6 +226,7 @@ export type InspectorOptions = Partial<{
225
226
  tags: string[]
226
227
  manifest: VersionManifest
227
228
  modelConfig: InspectorModelConfig
229
+ oldProgram: ts.Program | undefined
228
230
  }>
229
231
 
230
232
  export interface InspectorLogger {
@@ -378,6 +380,10 @@ export interface InspectorState {
378
380
  definitions: SecretDefinitions
379
381
  files: Set<string>
380
382
  }
383
+ credentials: {
384
+ definitions: CredentialDefinitions
385
+ files: Set<string>
386
+ }
381
387
  variables: {
382
388
  definitions: VariableDefinitions
383
389
  files: Set<string>
@@ -420,4 +426,5 @@ export interface InspectorState {
420
426
  openAPISpec: Record<string, any> | null
421
427
  diagnostics: InspectorDiagnostic[]
422
428
  addonFunctions: Record<string, FunctionsMeta> // namespace -> addon's function metadata
429
+ program: ts.Program | null // Retained for incremental re-inspection
423
430
  }
@@ -16,6 +16,7 @@ export function generateCustomTypes(
16
16
  requiredTypes: Set<string>
17
17
  ) {
18
18
  const typeDeclarations = Array.from(typesMap.customTypes.entries())
19
+ .sort(([a], [b]) => a.localeCompare(b))
19
20
  .filter(([_name, { type }]) => {
20
21
  const hasUndefinedGeneric =
21
22
  /\b(Name|In|Out|Key)\b/.test(type) && /\[.*\]/.test(type)
@@ -492,6 +492,65 @@ export function validateAgentOverrides(
492
492
  }
493
493
  }
494
494
 
495
+ /**
496
+ * Validates that Zod schemas and wiring side-effects (wireHTTPRoutes,
497
+ * addPermission, addHTTPMiddleware, etc.) do not coexist in the same file.
498
+ *
499
+ * The CLI uses tsImport to extract Zod schemas at runtime, which executes
500
+ * all top-level code in the file. Wiring calls crash during this process
501
+ * because the pikku state metadata doesn't exist in the CLI context.
502
+ */
503
+ export function validateSchemaWiringSeparation(
504
+ logger: InspectorLogger,
505
+ state: InspectorState
506
+ ): void {
507
+ // Collect files that contain schemas
508
+ const schemaFiles = new Set<string>()
509
+ for (const ref of state.schemaLookup.values()) {
510
+ schemaFiles.add(ref.sourceFile)
511
+ }
512
+
513
+ // Collect files that contain wiring side-effects
514
+ const wiringFiles = new Set<string>()
515
+
516
+ // HTTP route wirings
517
+ for (const file of state.http.files) {
518
+ wiringFiles.add(file)
519
+ }
520
+
521
+ // Permission wirings (addPermission calls)
522
+ for (const meta of state.permissions.tagPermissions.values()) {
523
+ wiringFiles.add(meta.sourceFile)
524
+ }
525
+ for (const meta of state.http.routePermissions.values()) {
526
+ wiringFiles.add(meta.sourceFile)
527
+ }
528
+
529
+ // Middleware wirings (addHTTPMiddleware calls)
530
+ for (const meta of state.http.routeMiddleware.values()) {
531
+ wiringFiles.add(meta.sourceFile)
532
+ }
533
+ for (const meta of state.middleware.tagMiddleware.values()) {
534
+ wiringFiles.add(meta.sourceFile)
535
+ }
536
+
537
+ // Check for overlap
538
+ for (const file of schemaFiles) {
539
+ if (wiringFiles.has(file)) {
540
+ const schemas = Array.from(state.schemaLookup.entries())
541
+ .filter(([, ref]) => ref.sourceFile === file)
542
+ .map(([name]) => name)
543
+
544
+ logger.critical(
545
+ ErrorCode.SCHEMA_AND_WIRING_COLOCATED,
546
+ `File '${file}' contains both Zod schemas (${schemas.join(', ')}) and wiring calls (wireHTTPRoutes, addPermission, etc.). ` +
547
+ `These must be in separate files because the CLI imports schema files at runtime, which triggers wiring side-effects that crash without server context. ` +
548
+ `Move the route/wiring definitions to a dedicated wiring file.`
549
+ )
550
+ }
551
+ }
552
+ }
553
+
495
554
  export function computeDiagnostics(state: InspectorState): void {
496
555
  const diagnostics: InspectorDiagnostic[] = []
497
556
 
@@ -63,24 +63,37 @@ function primitiveTypeToSchema(typeStr: string): JSONValue | null {
63
63
  return null
64
64
  }
65
65
 
66
+ // Cached state for schema program reuse across inspect() calls
67
+ let cachedSchemaProgram: ts.Program | undefined
68
+ let cachedParsedConfig: ts.ParsedCommandLine | undefined
69
+ let cachedTsconfigPath: string | undefined
70
+ let cachedCustomTypesContent: string | undefined
71
+ let cachedTSSchemas: Record<string, JSONValue> | undefined
72
+
66
73
  function createProgramWithVirtualFile(
67
74
  tsconfig: string,
68
75
  virtualFilePath: string,
69
76
  virtualFileContent: string
70
77
  ): ts.Program {
71
78
  const configPath = resolve(tsconfig)
72
- const configFile = ts.readConfigFile(configPath, ts.sys.readFile)
73
- const basePath = dirname(configPath)
74
- const parsedConfig = ts.parseJsonConfigFileContent(
75
- configFile.config,
76
- ts.sys,
77
- basePath
78
- )
79
+
80
+ // Cache the parsed tsconfig — it doesn't change between runs
81
+ if (!cachedParsedConfig || cachedTsconfigPath !== configPath) {
82
+ const configFile = ts.readConfigFile(configPath, ts.sys.readFile)
83
+ const basePath = dirname(configPath)
84
+ cachedParsedConfig = ts.parseJsonConfigFileContent(
85
+ configFile.config,
86
+ ts.sys,
87
+ basePath
88
+ )
89
+ cachedTsconfigPath = configPath
90
+ cachedSchemaProgram = undefined
91
+ }
79
92
 
80
93
  const resolvedVirtualPath = resolve(virtualFilePath)
81
- const fileNames = [...parsedConfig.fileNames, resolvedVirtualPath]
94
+ const fileNames = [...cachedParsedConfig.fileNames, resolvedVirtualPath]
82
95
 
83
- const defaultHost = ts.createCompilerHost(parsedConfig.options)
96
+ const defaultHost = ts.createCompilerHost(cachedParsedConfig.options)
84
97
  const customHost: ts.CompilerHost = {
85
98
  ...defaultHost,
86
99
  getSourceFile(
@@ -113,7 +126,14 @@ function createProgramWithVirtualFile(
113
126
  },
114
127
  }
115
128
 
116
- return ts.createProgram(fileNames, parsedConfig.options, customHost)
129
+ const program = ts.createProgram(
130
+ fileNames,
131
+ cachedParsedConfig.options,
132
+ customHost,
133
+ cachedSchemaProgram // reuse previous program for incremental compilation
134
+ )
135
+ cachedSchemaProgram = program
136
+ return program
117
137
  }
118
138
 
119
139
  function generateTSSchemas(
@@ -313,6 +333,11 @@ export async function generateAllSchemas(
313
333
  requiredTypes
314
334
  )
315
335
 
336
+ if (cachedTSSchemas && cachedCustomTypesContent === customTypesContent) {
337
+ logger.debug('Reusing cached TS schemas (types unchanged)')
338
+ return { ...cachedTSSchemas, ...zodSchemas }
339
+ }
340
+
316
341
  const tsSchemas = generateTSSchemas(
317
342
  logger,
318
343
  config.tsconfig,
@@ -325,5 +350,8 @@ export async function generateAllSchemas(
325
350
  state.schemaLookup
326
351
  )
327
352
 
353
+ cachedCustomTypesContent = customTypesContent
354
+ cachedTSSchemas = tsSchemas
355
+
328
356
  return { ...tsSchemas, ...zodSchemas }
329
357
  }
@@ -183,6 +183,10 @@ export interface SerializableInspectorState {
183
183
  definitions: InspectorState['secrets']['definitions']
184
184
  files: string[]
185
185
  }
186
+ credentials: {
187
+ definitions: InspectorState['credentials']['definitions']
188
+ files: string[]
189
+ }
186
190
  variables: {
187
191
  definitions: InspectorState['variables']['definitions']
188
192
  files: string[]
@@ -380,6 +384,10 @@ export function serializeInspectorState(
380
384
  definitions: state.secrets.definitions,
381
385
  files: Array.from(state.secrets.files),
382
386
  },
387
+ credentials: {
388
+ definitions: state.credentials.definitions,
389
+ files: Array.from(state.credentials.files),
390
+ },
383
391
  variables: {
384
392
  definitions: state.variables.definitions,
385
393
  files: Array.from(state.variables.files),
@@ -548,6 +556,10 @@ export function deserializeInspectorState(
548
556
  definitions: data.secrets?.definitions || [],
549
557
  files: new Set(data.secrets?.files || []),
550
558
  },
559
+ credentials: {
560
+ definitions: data.credentials?.definitions || [],
561
+ files: new Set(data.credentials?.files || []),
562
+ },
551
563
  variables: {
552
564
  definitions: data.variables?.definitions || [],
553
565
  files: new Set(data.variables?.files || []),
@@ -596,5 +608,6 @@ export function deserializeInspectorState(
596
608
  openAPISpec: data.openAPISpec || null,
597
609
  diagnostics: data.diagnostics || [],
598
610
  addonFunctions: data.addonFunctions || {},
611
+ program: null,
599
612
  }
600
613
  }
@@ -400,6 +400,7 @@ export function convertDslToGraph(
400
400
  source,
401
401
  description: meta.description,
402
402
  tags: meta.tags,
403
+ inline: meta.inline,
403
404
  context: meta.context,
404
405
  nodes: nodesRecord,
405
406
  entryNodeIds,
@@ -192,6 +192,8 @@ export interface SerializedWorkflowGraph {
192
192
  description?: string
193
193
  /** Tags for organization */
194
194
  tags?: string[]
195
+ /** If true, workflow always executes inline without queues */
196
+ inline?: boolean
195
197
  /** Workflow context/state variables (from Zod schema) */
196
198
  context?: WorkflowContext
197
199
  /** Serialized nodes */
package/src/visit.ts CHANGED
@@ -22,7 +22,8 @@ import { addWireAddon } from './add/add-wire-addon.js'
22
22
  import { addMiddleware } from './add/add-middleware.js'
23
23
  import { addPermission } from './add/add-permission.js'
24
24
  import { addCLI, addCLIRenderers } from './add/add-cli.js'
25
- import { addSecret, addOAuth2Credential } from './add/add-secret.js'
25
+ import { addSecret } from './add/add-secret.js'
26
+ import { addCredential } from './add/add-credential.js'
26
27
  import { addVariable } from './add/add-variable.js'
27
28
  import { addWorkflowGraph } from './add/add-workflow-graph.js'
28
29
  import { addAIAgent } from './add/add-ai-agent.js'
@@ -105,7 +106,7 @@ export const visitRoutes = (
105
106
  ) => {
106
107
  addFunctions(logger, node, checker, state, options)
107
108
  addSecret(logger, node, checker, state, options)
108
- addOAuth2Credential(logger, node, checker, state, options)
109
+ addCredential(logger, node, checker, state, options)
109
110
  addVariable(logger, node, checker, state, options)
110
111
 
111
112
  addHTTPRoute(logger, node, checker, state, options)