@pikku/inspector 0.11.1 → 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 (189) hide show
  1. package/CHANGELOG.md +26 -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 +327 -59
  9. package/dist/add/add-http-route.d.ts +19 -10
  10. package/dist/add/add-http-route.js +153 -44
  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.d.ts +3 -0
  23. package/dist/add/add-rpc-invocations.js +65 -25
  24. package/dist/add/add-schedule.js +11 -5
  25. package/dist/add/add-secret.d.ts +3 -0
  26. package/dist/add/add-secret.js +82 -0
  27. package/dist/add/add-trigger.d.ts +2 -0
  28. package/dist/add/add-trigger.js +87 -0
  29. package/dist/add/add-variable.d.ts +1 -0
  30. package/dist/add/add-variable.js +8 -0
  31. package/dist/add/add-workflow-graph.d.ts +7 -0
  32. package/dist/add/add-workflow-graph.js +396 -0
  33. package/dist/add/add-workflow.js +124 -26
  34. package/dist/error-codes.d.ts +16 -1
  35. package/dist/error-codes.js +21 -1
  36. package/dist/index.d.ts +9 -5
  37. package/dist/index.js +5 -2
  38. package/dist/inspector.d.ts +1 -1
  39. package/dist/inspector.js +106 -13
  40. package/dist/schema-generator.d.ts +1 -0
  41. package/dist/schema-generator.js +1 -0
  42. package/dist/types-map.js +10 -1
  43. package/dist/types.d.ts +180 -30
  44. package/dist/utils/compute-required-schemas.d.ts +4 -0
  45. package/dist/utils/compute-required-schemas.js +41 -0
  46. package/dist/utils/contract-hashes.d.ts +35 -0
  47. package/dist/utils/contract-hashes.js +202 -0
  48. package/dist/utils/custom-types-generator.d.ts +9 -0
  49. package/dist/utils/custom-types-generator.js +71 -0
  50. package/dist/utils/detect-schema-vendor.d.ts +22 -0
  51. package/dist/utils/detect-schema-vendor.js +76 -0
  52. package/dist/utils/ensure-function-metadata.d.ts +5 -2
  53. package/dist/utils/ensure-function-metadata.js +220 -6
  54. package/dist/utils/extract-function-name.d.ts +5 -16
  55. package/dist/utils/extract-function-name.js +93 -298
  56. package/dist/utils/extract-services.d.ts +2 -1
  57. package/dist/utils/extract-services.js +25 -1
  58. package/dist/utils/filter-inspector-state.js +107 -23
  59. package/dist/utils/get-property-value.d.ts +8 -2
  60. package/dist/utils/get-property-value.js +33 -4
  61. package/dist/utils/hash.d.ts +2 -0
  62. package/dist/utils/hash.js +23 -0
  63. package/dist/utils/middleware.d.ts +7 -30
  64. package/dist/utils/middleware.js +80 -66
  65. package/dist/utils/permissions.d.ts +2 -2
  66. package/dist/utils/permissions.js +10 -10
  67. package/dist/utils/post-process.d.ts +9 -10
  68. package/dist/utils/post-process.js +231 -24
  69. package/dist/utils/resolve-external-package.d.ts +12 -0
  70. package/dist/utils/resolve-external-package.js +34 -0
  71. package/dist/utils/resolve-function-types.d.ts +6 -0
  72. package/dist/utils/resolve-function-types.js +29 -0
  73. package/dist/utils/resolve-identifier.d.ts +10 -0
  74. package/dist/utils/resolve-identifier.js +36 -0
  75. package/dist/utils/resolve-versions.d.ts +2 -0
  76. package/dist/utils/resolve-versions.js +78 -0
  77. package/dist/utils/schema-generator.d.ts +9 -0
  78. package/dist/utils/schema-generator.js +209 -0
  79. package/dist/utils/serialize-inspector-state.d.ts +73 -13
  80. package/dist/utils/serialize-inspector-state.js +102 -6
  81. package/dist/utils/serialize-mcp-json.d.ts +2 -0
  82. package/dist/utils/serialize-mcp-json.js +99 -0
  83. package/dist/utils/serialize-middleware-groups-meta.d.ts +12 -0
  84. package/dist/utils/serialize-middleware-groups-meta.js +28 -0
  85. package/dist/utils/serialize-openapi-json.d.ts +85 -0
  86. package/dist/utils/serialize-openapi-json.js +151 -0
  87. package/dist/utils/serialize-permissions-groups-meta.d.ts +6 -0
  88. package/dist/utils/serialize-permissions-groups-meta.js +31 -0
  89. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.d.ts +24 -0
  90. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +830 -0
  91. package/dist/{workflow/extract-simple-workflow.d.ts → utils/workflow/dsl/extract-dsl-workflow.d.ts} +4 -2
  92. package/dist/{workflow/extract-simple-workflow.js → utils/workflow/dsl/extract-dsl-workflow.js} +572 -72
  93. package/dist/utils/workflow/dsl/index.d.ts +7 -0
  94. package/dist/utils/workflow/dsl/index.js +7 -0
  95. package/dist/{workflow → utils/workflow/dsl}/patterns.d.ts +21 -0
  96. package/dist/{workflow → utils/workflow/dsl}/patterns.js +90 -10
  97. package/dist/{workflow → utils/workflow/dsl}/validation.d.ts +2 -0
  98. package/dist/{workflow → utils/workflow/dsl}/validation.js +25 -7
  99. package/dist/utils/workflow/graph/convert-dsl-to-graph.d.ts +13 -0
  100. package/dist/utils/workflow/graph/convert-dsl-to-graph.js +318 -0
  101. package/dist/utils/workflow/graph/finalize-workflow-wires.d.ts +3 -0
  102. package/dist/utils/workflow/graph/finalize-workflow-wires.js +276 -0
  103. package/dist/utils/workflow/graph/finalize-workflows.d.ts +2 -0
  104. package/dist/utils/workflow/graph/finalize-workflows.js +75 -0
  105. package/dist/utils/workflow/graph/index.d.ts +8 -0
  106. package/dist/utils/workflow/graph/index.js +8 -0
  107. package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +35 -0
  108. package/dist/utils/workflow/graph/serialize-workflow-graph.js +150 -0
  109. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +203 -0
  110. package/dist/utils/workflow/graph/workflow-graph.types.js +38 -0
  111. package/dist/visit.js +13 -2
  112. package/package.json +26 -4
  113. package/src/add/add-ai-agent.ts +468 -0
  114. package/src/add/add-channel.ts +82 -79
  115. package/src/add/add-cli.ts +49 -20
  116. package/src/add/add-file-with-factory.ts +2 -0
  117. package/src/add/add-functions.ts +429 -71
  118. package/src/add/add-http-route.ts +246 -65
  119. package/src/add/add-http-routes.ts +228 -0
  120. package/src/add/add-keyed-wiring.ts +151 -0
  121. package/src/add/add-mcp-prompt.ts +26 -15
  122. package/src/add/add-mcp-resource.ts +27 -15
  123. package/src/add/add-middleware.ts +482 -80
  124. package/src/add/add-permission.ts +199 -40
  125. package/src/add/add-queue-worker.ts +24 -19
  126. package/src/add/add-rpc-invocations.ts +78 -31
  127. package/src/add/add-schedule.ts +16 -11
  128. package/src/add/add-secret.ts +140 -0
  129. package/src/add/add-trigger.ts +154 -0
  130. package/src/add/add-variable.ts +9 -0
  131. package/src/add/add-workflow-graph.ts +522 -0
  132. package/src/add/add-workflow.ts +117 -30
  133. package/src/error-codes.ts +26 -1
  134. package/src/index.ts +27 -8
  135. package/src/inspector.ts +145 -17
  136. package/src/schema-generator.ts +1 -0
  137. package/src/types-map.ts +12 -1
  138. package/src/types.ts +192 -51
  139. package/src/utils/compute-required-schemas.ts +49 -0
  140. package/src/utils/contract-hashes.test.ts +528 -0
  141. package/src/utils/contract-hashes.ts +290 -0
  142. package/src/utils/custom-types-generator.ts +88 -0
  143. package/src/utils/detect-schema-vendor.ts +90 -0
  144. package/src/utils/ensure-function-metadata.ts +324 -7
  145. package/src/utils/extract-function-name.ts +108 -358
  146. package/src/utils/extract-services.ts +35 -2
  147. package/src/utils/filter-inspector-state.test.ts +34 -20
  148. package/src/utils/filter-inspector-state.ts +140 -31
  149. package/src/utils/get-property-value.ts +50 -5
  150. package/src/utils/hash.ts +26 -0
  151. package/src/utils/middleware.test.ts +204 -0
  152. package/src/utils/middleware.ts +129 -67
  153. package/src/utils/permissions.test.ts +35 -12
  154. package/src/utils/permissions.ts +10 -10
  155. package/src/utils/post-process.ts +283 -43
  156. package/src/utils/resolve-external-package.ts +42 -0
  157. package/src/utils/resolve-function-types.ts +42 -0
  158. package/src/utils/resolve-identifier.ts +46 -0
  159. package/src/utils/resolve-versions.test.ts +249 -0
  160. package/src/utils/resolve-versions.ts +105 -0
  161. package/src/utils/schema-generator.ts +329 -0
  162. package/src/utils/serialize-inspector-state.ts +181 -20
  163. package/src/utils/serialize-mcp-json.ts +145 -0
  164. package/src/utils/serialize-middleware-groups-meta.ts +33 -0
  165. package/src/utils/serialize-openapi-json.ts +277 -0
  166. package/src/utils/serialize-permissions-groups-meta.ts +35 -0
  167. package/src/utils/test-data/inspector-state.json +69 -66
  168. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +1104 -0
  169. package/src/{workflow/extract-simple-workflow.ts → utils/workflow/dsl/extract-dsl-workflow.ts} +678 -85
  170. package/src/utils/workflow/dsl/index.ts +11 -0
  171. package/src/{workflow → utils/workflow/dsl}/patterns.ts +108 -11
  172. package/src/{workflow → utils/workflow/dsl}/validation.ts +34 -7
  173. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +422 -0
  174. package/src/utils/workflow/graph/finalize-workflow-wires.ts +310 -0
  175. package/src/utils/workflow/graph/finalize-workflows.ts +100 -0
  176. package/src/utils/workflow/graph/index.ts +11 -0
  177. package/src/utils/workflow/graph/serialize-workflow-graph.ts +216 -0
  178. package/src/utils/workflow/graph/workflow-graph.types.ts +231 -0
  179. package/src/visit.ts +14 -2
  180. package/tsconfig.tsbuildinfo +1 -1
  181. package/dist/add/add-mcp-tool.d.ts +0 -2
  182. package/dist/add/add-mcp-tool.js +0 -81
  183. package/dist/utils/extract-service-metadata.d.ts +0 -19
  184. package/dist/utils/extract-service-metadata.js +0 -244
  185. package/dist/utils/write-service-metadata.d.ts +0 -13
  186. package/dist/utils/write-service-metadata.js +0 -37
  187. package/src/add/add-mcp-tool.ts +0 -141
  188. package/src/utils/extract-service-metadata.ts +0 -353
  189. package/src/utils/write-service-metadata.ts +0 -51
package/dist/types.d.ts CHANGED
@@ -2,13 +2,21 @@ import * as ts from 'typescript';
2
2
  import { ChannelsMeta } from '@pikku/core/channel';
3
3
  import { HTTPWiringsMeta } from '@pikku/core/http';
4
4
  import { ScheduledTasksMeta } from '@pikku/core/scheduler';
5
+ import { TriggerMeta, TriggerSourceMeta } from '@pikku/core/trigger';
5
6
  import { QueueWorkersMeta } from '@pikku/core/queue';
6
7
  import { WorkflowsMeta } from '@pikku/core/workflow';
7
8
  import { MCPResourceMeta, MCPToolMeta, MCPPromptMeta } from '@pikku/core/mcp';
9
+ import { AIAgentMeta } from '@pikku/core/ai-agent';
8
10
  import { CLIMeta } from '@pikku/core/cli';
11
+ import { NodesMeta } from '@pikku/core/node';
12
+ import { SecretDefinitions } from '@pikku/core/secret';
13
+ import { VariableDefinitions } from '@pikku/core/variable';
9
14
  import { TypesMap } from './types-map.js';
10
- import { FunctionsMeta, FunctionServicesMeta } from '@pikku/core';
15
+ import { FunctionsMeta, FunctionServicesMeta, FunctionWiresMeta, JSONValue } from '@pikku/core';
16
+ import type { OpenAPISpecInfo } from './utils/serialize-openapi-json.js';
11
17
  import { ErrorCode } from './error-codes.js';
18
+ import type { VersionManifest, VersionValidateError } from './utils/contract-hashes.js';
19
+ import type { SerializedWorkflowGraphs } from './utils/workflow/graph/workflow-graph.types.js';
12
20
  export type PathToNameAndType = Map<string, {
13
21
  variable: string;
14
22
  type: string | null;
@@ -24,7 +32,8 @@ export interface MiddlewareGroupMeta {
24
32
  sourceFile: string;
25
33
  position: number;
26
34
  services: FunctionServicesMeta;
27
- middlewareCount: number;
35
+ count: number;
36
+ instanceIds: string[];
28
37
  isFactory: boolean;
29
38
  }
30
39
  export interface PermissionGroupMeta {
@@ -32,7 +41,8 @@ export interface PermissionGroupMeta {
32
41
  sourceFile: string;
33
42
  position: number;
34
43
  services: FunctionServicesMeta;
35
- permissionCount: number;
44
+ count: number;
45
+ instanceIds: string[];
36
46
  isFactory: boolean;
37
47
  }
38
48
  export interface InspectorHTTPState {
@@ -42,6 +52,19 @@ export interface InspectorHTTPState {
42
52
  routeMiddleware: Map<string, MiddlewareGroupMeta>;
43
53
  routePermissions: Map<string, PermissionGroupMeta>;
44
54
  }
55
+ /**
56
+ * Schema vendor types for Standard Schema compliant validators
57
+ */
58
+ export type SchemaVendor = 'zod' | 'valibot' | 'arktype' | 'effect' | 'unknown';
59
+ /**
60
+ * Schema reference for deferred conversion to JSON Schema at build time.
61
+ * Supports Standard Schema compliant validators (Zod, Valibot, ArkType, Effect Schema).
62
+ */
63
+ export interface SchemaRef {
64
+ variableName: string;
65
+ sourceFile: string;
66
+ vendor?: SchemaVendor;
67
+ }
45
68
  export interface InspectorFunctionState {
46
69
  typesMap: TypesMap;
47
70
  meta: FunctionsMeta;
@@ -54,28 +77,57 @@ export interface InspectorChannelState {
54
77
  meta: ChannelsMeta;
55
78
  files: Set<string>;
56
79
  }
80
+ export interface InspectorMiddlewareDefinition {
81
+ services: FunctionServicesMeta;
82
+ wires?: FunctionWiresMeta;
83
+ sourceFile: string;
84
+ position: number;
85
+ exportedName: string | null;
86
+ factory?: boolean;
87
+ name?: string;
88
+ description?: string;
89
+ package?: string;
90
+ }
91
+ export interface InspectorMiddlewareInstance {
92
+ definitionId: string;
93
+ sourceFile: string;
94
+ position: number;
95
+ isFactoryCall: boolean;
96
+ }
57
97
  export interface InspectorMiddlewareState {
58
- meta: Record<string, {
59
- services: FunctionServicesMeta;
60
- sourceFile: string;
61
- position: number;
62
- exportedName: string | null;
63
- factory?: boolean;
64
- name?: string;
65
- description?: string;
66
- }>;
98
+ definitions: Record<string, InspectorMiddlewareDefinition>;
99
+ instances: Record<string, InspectorMiddlewareInstance>;
67
100
  tagMiddleware: Map<string, MiddlewareGroupMeta>;
68
101
  }
102
+ export interface InspectorChannelMiddlewareState {
103
+ definitions: Record<string, InspectorMiddlewareDefinition>;
104
+ instances: Record<string, InspectorMiddlewareInstance>;
105
+ tagMiddleware: Map<string, MiddlewareGroupMeta>;
106
+ }
107
+ export interface InspectorAIMiddlewareState {
108
+ definitions: Record<string, InspectorMiddlewareDefinition>;
109
+ }
110
+ export interface InspectorPermissionDefinition {
111
+ services: FunctionServicesMeta;
112
+ wires?: FunctionWiresMeta;
113
+ sourceFile: string;
114
+ position: number;
115
+ exportedName: string | null;
116
+ factory?: boolean;
117
+ name?: string;
118
+ description?: string;
119
+ package?: string;
120
+ requiresData?: boolean;
121
+ }
122
+ export interface InspectorPermissionInstance {
123
+ definitionId: string;
124
+ sourceFile: string;
125
+ position: number;
126
+ isFactoryCall: boolean;
127
+ }
69
128
  export interface InspectorPermissionState {
70
- meta: Record<string, {
71
- services: FunctionServicesMeta;
72
- sourceFile: string;
73
- position: number;
74
- exportedName: string | null;
75
- factory?: boolean;
76
- name?: string;
77
- description?: string;
78
- }>;
129
+ definitions: Record<string, InspectorPermissionDefinition>;
130
+ instances: Record<string, InspectorPermissionInstance>;
79
131
  tagPermissions: Map<string, PermissionGroupMeta>;
80
132
  }
81
133
  export type InspectorFilters = {
@@ -86,14 +138,53 @@ export type InspectorFilters = {
86
138
  httpRoutes?: string[];
87
139
  httpMethods?: string[];
88
140
  };
141
+ export type ExternalPackageConfig = {
142
+ package: string;
143
+ rpcEndpoint?: string;
144
+ secretOverrides?: Record<string, string>;
145
+ forceInclude?: boolean;
146
+ };
147
+ export type ModelConfigEntry = string | {
148
+ model: string;
149
+ temperature?: number;
150
+ maxSteps?: number;
151
+ };
152
+ export type InspectorModelConfig = {
153
+ models?: Record<string, ModelConfigEntry>;
154
+ agentDefaults?: {
155
+ temperature?: number;
156
+ maxSteps?: number;
157
+ };
158
+ agentOverrides?: Record<string, {
159
+ model?: string;
160
+ temperature?: number;
161
+ maxSteps?: number;
162
+ }>;
163
+ };
89
164
  export type InspectorOptions = Partial<{
90
165
  setupOnly: boolean;
166
+ rootDir: string;
167
+ isExternalPackage: boolean;
91
168
  types: Partial<{
92
169
  configFileType: string;
93
170
  userSessionType: string;
94
171
  singletonServicesFactoryType: string;
95
172
  wireServicesFactoryType: string;
96
173
  }>;
174
+ externalPackages: Record<string, ExternalPackageConfig>;
175
+ schemaConfig: {
176
+ tsconfig: string;
177
+ schemasFromTypes?: string[];
178
+ schema?: {
179
+ additionalProperties?: boolean;
180
+ };
181
+ };
182
+ openAPI: {
183
+ additionalInfo: OpenAPISpecInfo;
184
+ };
185
+ tags: string[];
186
+ manifest: VersionManifest;
187
+ modelConfig: InspectorModelConfig;
97
188
  }>;
98
189
  export interface InspectorLogger {
99
190
  info: (message: string) => void;
@@ -148,6 +239,12 @@ export interface InspectorFilesAndMethods {
148
239
  typePath: string;
149
240
  };
150
241
  }
242
+ export interface InspectorDiagnostic {
243
+ code: string;
244
+ message: string;
245
+ sourceFile: string;
246
+ position: number;
247
+ }
151
248
  export interface InspectorState {
152
249
  rootDir: string;
153
250
  singletonServicesTypeImportMap: PathToNameAndType;
@@ -161,9 +258,16 @@ export interface InspectorState {
161
258
  filesAndMethods: InspectorFilesAndMethods;
162
259
  filesAndMethodsErrors: Map<string, PathToNameAndType>;
163
260
  typesLookup: Map<string, ts.Type[]>;
261
+ schemaLookup: Map<string, SchemaRef>;
262
+ schemas: Record<string, JSONValue>;
164
263
  http: InspectorHTTPState;
165
264
  functions: InspectorFunctionState;
166
265
  channels: InspectorChannelState;
266
+ triggers: {
267
+ meta: TriggerMeta;
268
+ sourceMeta: TriggerSourceMeta;
269
+ files: Set<string>;
270
+ };
167
271
  scheduledTasks: {
168
272
  meta: ScheduledTasksMeta;
169
273
  files: Set<string>;
@@ -178,6 +282,12 @@ export interface InspectorState {
178
282
  path: string;
179
283
  exportedName: string;
180
284
  }>;
285
+ graphMeta: SerializedWorkflowGraphs;
286
+ graphFiles: Map<string, {
287
+ path: string;
288
+ exportedName: string;
289
+ }>;
290
+ invokedWorkflows: Set<string>;
181
291
  };
182
292
  rpc: {
183
293
  internalMeta: Record<string, string>;
@@ -191,6 +301,7 @@ export interface InspectorState {
191
301
  exportedName: string;
192
302
  }>;
193
303
  invokedFunctions: Set<string>;
304
+ usedExternalPackages: Set<string>;
194
305
  };
195
306
  mcpEndpoints: {
196
307
  resourcesMeta: MCPResourceMeta;
@@ -198,11 +309,37 @@ export interface InspectorState {
198
309
  promptsMeta: MCPPromptMeta;
199
310
  files: Set<string>;
200
311
  };
312
+ agents: {
313
+ agentsMeta: AIAgentMeta;
314
+ files: Map<string, {
315
+ path: string;
316
+ exportedName: string;
317
+ }>;
318
+ };
201
319
  cli: {
202
320
  meta: CLIMeta;
203
321
  files: Set<string>;
204
322
  };
323
+ nodes: {
324
+ meta: NodesMeta;
325
+ files: Set<string>;
326
+ };
327
+ secrets: {
328
+ definitions: SecretDefinitions;
329
+ files: Set<string>;
330
+ };
331
+ variables: {
332
+ definitions: VariableDefinitions;
333
+ files: Set<string>;
334
+ };
335
+ manifest: {
336
+ initial: VersionManifest | null;
337
+ current: VersionManifest | null;
338
+ errors: VersionValidateError[];
339
+ };
205
340
  middleware: InspectorMiddlewareState;
341
+ channelMiddleware: InspectorChannelMiddlewareState;
342
+ aiMiddleware: InspectorAIMiddlewareState;
206
343
  permissions: InspectorPermissionState;
207
344
  serviceAggregation: {
208
345
  requiredServices: Set<string>;
@@ -212,14 +349,27 @@ export interface InspectorState {
212
349
  allSingletonServices: string[];
213
350
  allWireServices: string[];
214
351
  };
215
- serviceMetadata: Array<{
216
- name: string;
217
- summary: string;
218
- description: string;
219
- package: string;
220
- path: string;
221
- version: string;
222
- interface: string;
223
- expandedProperties: Record<string, string>;
352
+ resolvedIOTypes: Record<string, {
353
+ inputType: string;
354
+ outputType: string;
224
355
  }>;
356
+ middlewareGroupsMeta: {
357
+ definitions: Record<string, InspectorMiddlewareDefinition>;
358
+ instances: Record<string, InspectorMiddlewareInstance>;
359
+ httpGroups: Record<string, MiddlewareGroupMeta>;
360
+ tagGroups: Record<string, MiddlewareGroupMeta>;
361
+ channelMiddleware: {
362
+ definitions: Record<string, InspectorMiddlewareDefinition>;
363
+ instances: Record<string, InspectorMiddlewareInstance>;
364
+ tagGroups: Record<string, MiddlewareGroupMeta>;
365
+ };
366
+ };
367
+ permissionsGroupsMeta: {
368
+ definitions: Record<string, InspectorPermissionDefinition>;
369
+ httpGroups: Record<string, PermissionGroupMeta>;
370
+ tagGroups: Record<string, PermissionGroupMeta>;
371
+ };
372
+ requiredSchemas: Set<string>;
373
+ openAPISpec: Record<string, any> | null;
374
+ diagnostics: InspectorDiagnostic[];
225
375
  }
@@ -0,0 +1,4 @@
1
+ import type { FunctionsMeta } from '@pikku/core';
2
+ import type { TypesMap } from '../types-map.js';
3
+ import type { SchemaRef } from '../types.js';
4
+ export declare function computeRequiredSchemas(functionsMeta: FunctionsMeta, typesMap: TypesMap, additionalTypes?: string[], schemaLookup?: Map<string, SchemaRef>): Set<string>;
@@ -0,0 +1,41 @@
1
+ const PRIMITIVE_TYPES = new Set([
2
+ 'boolean',
3
+ 'string',
4
+ 'number',
5
+ 'null',
6
+ 'undefined',
7
+ 'void',
8
+ 'any',
9
+ 'unknown',
10
+ 'never',
11
+ ]);
12
+ export function computeRequiredSchemas(functionsMeta, typesMap, additionalTypes, schemaLookup) {
13
+ return new Set([
14
+ ...Object.values(functionsMeta)
15
+ .map(({ inputs, outputs }) => {
16
+ const types = [];
17
+ if (inputs?.[0]) {
18
+ try {
19
+ types.push(typesMap.getUniqueName(inputs[0]));
20
+ }
21
+ catch {
22
+ types.push(inputs[0]);
23
+ }
24
+ }
25
+ if (outputs?.[0]) {
26
+ try {
27
+ types.push(typesMap.getUniqueName(outputs[0]));
28
+ }
29
+ catch {
30
+ types.push(outputs[0]);
31
+ }
32
+ }
33
+ return types;
34
+ })
35
+ .flat()
36
+ .filter((s) => !!s && !PRIMITIVE_TYPES.has(s)),
37
+ ...typesMap.customTypes.keys(),
38
+ ...(additionalTypes || []),
39
+ ...(schemaLookup ? Array.from(schemaLookup.keys()) : []),
40
+ ]);
41
+ }
@@ -0,0 +1,35 @@
1
+ import { FunctionsMeta, JSONValue } from '@pikku/core';
2
+ import { TypesMap } from '../types-map.js';
3
+ import { ErrorCode } from '../error-codes.js';
4
+ export type ContractEntry = {
5
+ functionKey: string;
6
+ version: number;
7
+ contractHash: string;
8
+ };
9
+ export type VersionValidateError = {
10
+ code: ErrorCode;
11
+ message: string;
12
+ };
13
+ export type VersionManifestEntry = {
14
+ latest: number;
15
+ versions: Record<string, string>;
16
+ };
17
+ export type VersionManifest = {
18
+ manifestVersion: 1;
19
+ contracts: Record<string, VersionManifestEntry>;
20
+ };
21
+ export declare function createEmptyManifest(): VersionManifest;
22
+ export declare function serializeManifest(manifest: VersionManifest): string;
23
+ export declare function computeContractHash(data: {
24
+ functionKey: string;
25
+ inputSchema: unknown;
26
+ outputSchema: unknown;
27
+ }): string;
28
+ export declare function buildCurrentContracts(functionsMeta: FunctionsMeta, allSchemas: Record<string, JSONValue>, typesMap: TypesMap): Map<string, ContractEntry>;
29
+ export declare function computeContractHashes(allSchemas: Record<string, JSONValue>, typesMap: TypesMap, functionsMeta: FunctionsMeta): Map<string, ContractEntry>;
30
+ export declare function validateContracts(manifest: VersionManifest, currentContracts: Map<string, ContractEntry>): {
31
+ valid: boolean;
32
+ errors: VersionValidateError[];
33
+ };
34
+ export declare function updateManifest(existing: VersionManifest, currentContracts: Map<string, ContractEntry>): VersionManifest;
35
+ export declare function extractContractsFromMeta(functionsMeta: FunctionsMeta): Map<string, ContractEntry>;
@@ -0,0 +1,202 @@
1
+ import { parseVersionedId } from '@pikku/core';
2
+ import { ErrorCode } from '../error-codes.js';
3
+ import { canonicalJSON, hashString } from './hash.js';
4
+ export function createEmptyManifest() {
5
+ return {
6
+ manifestVersion: 1,
7
+ contracts: {},
8
+ };
9
+ }
10
+ export function serializeManifest(manifest) {
11
+ const sortedContracts = {};
12
+ for (const key of Object.keys(manifest.contracts).sort()) {
13
+ const entry = manifest.contracts[key];
14
+ const sortedVersions = {};
15
+ const numericKeys = Object.keys(entry.versions)
16
+ .map(Number)
17
+ .sort((a, b) => a - b);
18
+ for (const vk of numericKeys) {
19
+ sortedVersions[String(vk)] = entry.versions[String(vk)];
20
+ }
21
+ sortedContracts[key] = {
22
+ latest: entry.latest,
23
+ versions: sortedVersions,
24
+ };
25
+ }
26
+ const sorted = {
27
+ manifestVersion: manifest.manifestVersion,
28
+ contracts: sortedContracts,
29
+ };
30
+ return JSON.stringify(sorted, null, 2) + '\n';
31
+ }
32
+ export function computeContractHash(data) {
33
+ return hashString(canonicalJSON(data), 16);
34
+ }
35
+ function resolveSchema(typeNames, allSchemas, typesMap) {
36
+ if (!typeNames) {
37
+ return null;
38
+ }
39
+ const filtered = typeNames.filter((n) => n !== 'void');
40
+ if (filtered.length === 0) {
41
+ return null;
42
+ }
43
+ const parts = [];
44
+ for (const name of filtered) {
45
+ let key;
46
+ try {
47
+ key = typesMap.getUniqueName(name);
48
+ }
49
+ catch {
50
+ key = name;
51
+ }
52
+ const schema = allSchemas[key];
53
+ if (schema) {
54
+ parts.push(schema);
55
+ }
56
+ }
57
+ if (parts.length === 0) {
58
+ return null;
59
+ }
60
+ return parts.length === 1 ? parts[0] : parts;
61
+ }
62
+ export function buildCurrentContracts(functionsMeta, allSchemas, typesMap) {
63
+ const result = new Map();
64
+ for (const [funcId, meta] of Object.entries(functionsMeta)) {
65
+ if (meta.remote === true) {
66
+ continue;
67
+ }
68
+ const parsed = parseVersionedId(funcId);
69
+ const functionKey = parsed.baseName;
70
+ const version = parsed.version ?? meta.version ?? 1;
71
+ const inputSchema = resolveSchema(meta.inputs, allSchemas, typesMap);
72
+ const outputSchema = resolveSchema(meta.outputs, allSchemas, typesMap);
73
+ const contractHash = computeContractHash({
74
+ functionKey,
75
+ inputSchema,
76
+ outputSchema,
77
+ });
78
+ result.set(funcId, { functionKey, version, contractHash });
79
+ }
80
+ return result;
81
+ }
82
+ export function computeContractHashes(allSchemas, typesMap, functionsMeta) {
83
+ const contracts = buildCurrentContracts(functionsMeta, allSchemas, typesMap);
84
+ for (const [funcId, entry] of contracts) {
85
+ const meta = functionsMeta[funcId];
86
+ if (meta) {
87
+ meta.contractHash = entry.contractHash;
88
+ }
89
+ }
90
+ return contracts;
91
+ }
92
+ function groupByFunctionKey(contracts) {
93
+ const grouped = new Map();
94
+ for (const entry of contracts.values()) {
95
+ const existing = grouped.get(entry.functionKey) ?? [];
96
+ existing.push(entry);
97
+ grouped.set(entry.functionKey, existing);
98
+ }
99
+ return grouped;
100
+ }
101
+ export function validateContracts(manifest, currentContracts) {
102
+ const errors = [];
103
+ const grouped = groupByFunctionKey(currentContracts);
104
+ const reportedKeys = new Set();
105
+ for (const [functionKey, entries] of grouped) {
106
+ const manifestEntry = manifest.contracts[functionKey];
107
+ if (!manifestEntry) {
108
+ continue;
109
+ }
110
+ for (const { version, contractHash } of entries) {
111
+ const existingHash = manifestEntry.versions[String(version)];
112
+ if (existingHash !== undefined) {
113
+ if (existingHash !== contractHash) {
114
+ reportedKeys.add(`${functionKey}@${version}`);
115
+ errors.push({
116
+ code: ErrorCode.FUNCTION_VERSION_MODIFIED,
117
+ message: `Contract for ${functionKey}@v${version} has changed (recorded: ${existingHash}, current: ${contractHash}). Existing versions are immutable.`,
118
+ });
119
+ }
120
+ }
121
+ else {
122
+ if (version <= manifestEntry.latest) {
123
+ errors.push({
124
+ code: ErrorCode.VERSION_REGRESSION_OR_CONFLICT,
125
+ message: `Version ${version} for ${functionKey} is <= latest (${manifestEntry.latest}) but not recorded. Possible merge conflict.`,
126
+ });
127
+ }
128
+ else if (version > manifestEntry.latest + 1) {
129
+ errors.push({
130
+ code: ErrorCode.VERSION_GAP_NOT_ALLOWED,
131
+ message: `Version ${version} for ${functionKey} skips versions. Latest is ${manifestEntry.latest}, next must be ${manifestEntry.latest + 1}.`,
132
+ });
133
+ }
134
+ }
135
+ }
136
+ }
137
+ for (const [functionKey, manifestEntry] of Object.entries(manifest.contracts)) {
138
+ const latestHash = manifestEntry.versions[String(manifestEntry.latest)];
139
+ const currentEntries = grouped.get(functionKey);
140
+ if (!currentEntries) {
141
+ continue;
142
+ }
143
+ const currentLatest = currentEntries.find((e) => e.version === manifestEntry.latest);
144
+ if (currentLatest &&
145
+ currentLatest.contractHash !== latestHash &&
146
+ !reportedKeys.has(`${functionKey}@${manifestEntry.latest}`)) {
147
+ errors.push({
148
+ code: ErrorCode.CONTRACT_CHANGED_REQUIRES_BUMP,
149
+ message: `Contract for ${functionKey} changed. Set \`version: ${manifestEntry.latest + 1}\` on the function or run 'pikku versions update'.`,
150
+ });
151
+ }
152
+ }
153
+ for (const [functionKey, manifestEntry] of Object.entries(manifest.contracts)) {
154
+ const numericKeys = Object.keys(manifestEntry.versions).map(Number);
155
+ if (numericKeys.length === 0) {
156
+ continue;
157
+ }
158
+ const maxVersion = Math.max(...numericKeys);
159
+ if (manifestEntry.latest !== maxVersion) {
160
+ errors.push({
161
+ code: ErrorCode.MANIFEST_INTEGRITY_ERROR,
162
+ message: `Manifest integrity error for ${functionKey}: latest field (${manifestEntry.latest}) inconsistent with version keys (max: ${maxVersion}).`,
163
+ });
164
+ }
165
+ }
166
+ return { valid: errors.length === 0, errors };
167
+ }
168
+ export function updateManifest(existing, currentContracts) {
169
+ const manifest = {
170
+ manifestVersion: existing.manifestVersion,
171
+ contracts: JSON.parse(JSON.stringify(existing.contracts)),
172
+ };
173
+ const grouped = groupByFunctionKey(currentContracts);
174
+ for (const [functionKey, entries] of grouped) {
175
+ if (!manifest.contracts[functionKey]) {
176
+ manifest.contracts[functionKey] = { latest: 0, versions: {} };
177
+ }
178
+ const entry = manifest.contracts[functionKey];
179
+ for (const { version, contractHash } of entries) {
180
+ entry.versions[String(version)] = contractHash;
181
+ entry.latest = Math.max(entry.latest, version);
182
+ }
183
+ }
184
+ return manifest;
185
+ }
186
+ export function extractContractsFromMeta(functionsMeta) {
187
+ const result = new Map();
188
+ for (const [funcId, meta] of Object.entries(functionsMeta)) {
189
+ if (meta.remote === true || !meta.contractHash) {
190
+ continue;
191
+ }
192
+ const parsed = parseVersionedId(funcId);
193
+ const functionKey = parsed.baseName;
194
+ const version = parsed.version ?? meta.version ?? 1;
195
+ result.set(funcId, {
196
+ functionKey,
197
+ version,
198
+ contractHash: meta.contractHash,
199
+ });
200
+ }
201
+ return result;
202
+ }
@@ -0,0 +1,9 @@
1
+ import { TypesMap } from '../types-map.js';
2
+ /**
3
+ * NOTE: Code generation normally belongs in @pikku/cli, not the inspector.
4
+ * This is here because the schema generator needs the custom types content
5
+ * as a virtual TypeScript source file (in-memory, no disk write) so that
6
+ * ts-json-schema-generator can discover inline/custom types from typesMap.
7
+ */
8
+ export declare function sanitizeTypeName(name: string): string;
9
+ export declare function generateCustomTypes(typesMap: TypesMap, requiredTypes: Set<string>): string;
@@ -0,0 +1,71 @@
1
+ /**
2
+ * NOTE: Code generation normally belongs in @pikku/cli, not the inspector.
3
+ * This is here because the schema generator needs the custom types content
4
+ * as a virtual TypeScript source file (in-memory, no disk write) so that
5
+ * ts-json-schema-generator can discover inline/custom types from typesMap.
6
+ */
7
+ export function sanitizeTypeName(name) {
8
+ return name.replace(/[^a-zA-Z0-9_$]/g, '_');
9
+ }
10
+ export function generateCustomTypes(typesMap, requiredTypes) {
11
+ const typeDeclarations = Array.from(typesMap.customTypes.entries())
12
+ .filter(([_name, { type }]) => {
13
+ const hasUndefinedGeneric = /\b(Name|In|Out|Key)\b/.test(type) && /\[.*\]/.test(type);
14
+ return !hasUndefinedGeneric;
15
+ })
16
+ .map(([originalName, { type, references }]) => {
17
+ const name = sanitizeTypeName(originalName);
18
+ references.forEach((refName) => {
19
+ if (refName !== '__object' && !refName.startsWith('__object_')) {
20
+ requiredTypes.add(refName);
21
+ }
22
+ });
23
+ const typeString = type;
24
+ const typeNameRegex = /\b[A-Z][a-zA-Z0-9]*\b/g;
25
+ const potentialTypes = typeString.match(typeNameRegex) || [];
26
+ potentialTypes.forEach((typeName) => {
27
+ if (typeString.includes(`"${typeName}"`) ||
28
+ [
29
+ 'Pick',
30
+ 'Omit',
31
+ 'Partial',
32
+ 'Required',
33
+ 'Record',
34
+ 'Readonly',
35
+ ].includes(typeName)) {
36
+ return;
37
+ }
38
+ try {
39
+ const typeMeta = typesMap.getTypeMeta(typeName);
40
+ if (typeMeta.path) {
41
+ requiredTypes.add(typeMeta.originalName);
42
+ }
43
+ }
44
+ catch {
45
+ // Type not found in map (ambient/builtin type)
46
+ }
47
+ });
48
+ if (name === type)
49
+ return null;
50
+ return `export type ${name} = ${type}`;
51
+ });
52
+ const importsByPath = new Map();
53
+ for (const typeName of requiredTypes) {
54
+ try {
55
+ const typeMeta = typesMap.getTypeMeta(typeName);
56
+ if (typeMeta.path) {
57
+ if (!importsByPath.has(typeMeta.path)) {
58
+ importsByPath.set(typeMeta.path, new Set());
59
+ }
60
+ importsByPath.get(typeMeta.path).add(typeMeta.originalName);
61
+ }
62
+ }
63
+ catch {
64
+ // Type not found in map
65
+ }
66
+ }
67
+ const importLines = Array.from(importsByPath.entries())
68
+ .map(([path, types]) => `import type { ${Array.from(types).join(', ')} } from '${path}'`)
69
+ .join('\n');
70
+ return `${importLines}\n\n${typeDeclarations.filter(Boolean).join('\n')}`;
71
+ }