@pikku/inspector 0.12.0 → 0.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/add/add-ai-agent.d.ts +1 -1
  3. package/dist/add/add-ai-agent.js +1 -1
  4. package/dist/add/add-channel.js +14 -0
  5. package/dist/add/add-cli.d.ts +1 -1
  6. package/dist/add/add-cli.js +29 -8
  7. package/dist/add/add-file-extends-core-type.d.ts +1 -1
  8. package/dist/add/add-file-with-config.d.ts +1 -1
  9. package/dist/add/add-file-with-factory.d.ts +1 -1
  10. package/dist/add/add-file-with-factory.js +2 -2
  11. package/dist/add/add-functions.d.ts +1 -1
  12. package/dist/add/add-functions.js +6 -7
  13. package/dist/add/add-gateway.d.ts +2 -0
  14. package/dist/add/add-gateway.js +57 -0
  15. package/dist/add/add-http-route.d.ts +3 -2
  16. package/dist/add/add-http-route.js +5 -1
  17. package/dist/add/add-http-routes.d.ts +1 -1
  18. package/dist/add/add-http-routes.js +1 -0
  19. package/dist/add/add-keyed-wiring.d.ts +1 -1
  20. package/dist/add/add-mcp-prompt.d.ts +1 -1
  21. package/dist/add/add-mcp-resource.d.ts +1 -1
  22. package/dist/add/add-queue-worker.d.ts +1 -1
  23. package/dist/add/add-rpc-invocations.d.ts +3 -3
  24. package/dist/add/add-rpc-invocations.js +10 -10
  25. package/dist/add/add-schedule.d.ts +1 -1
  26. package/dist/add/add-secret.d.ts +1 -1
  27. package/dist/add/add-trigger.d.ts +1 -1
  28. package/dist/add/add-trigger.js +2 -2
  29. package/dist/add/add-wire-addon.d.ts +7 -0
  30. package/dist/add/add-wire-addon.js +70 -0
  31. package/dist/add/add-workflow.d.ts +1 -1
  32. package/dist/error-codes.d.ts +1 -0
  33. package/dist/error-codes.js +1 -0
  34. package/dist/index.d.ts +1 -1
  35. package/dist/inspector.d.ts +1 -1
  36. package/dist/inspector.js +11 -4
  37. package/dist/types.d.ts +31 -20
  38. package/dist/utils/compute-required-schemas.js +1 -2
  39. package/dist/utils/contract-hashes.d.ts +20 -3
  40. package/dist/utils/contract-hashes.js +77 -10
  41. package/dist/utils/custom-types-generator.d.ts +1 -1
  42. package/dist/utils/detect-schema-vendor.d.ts +1 -1
  43. package/dist/utils/does-type-extend-core-type.d.ts +1 -1
  44. package/dist/utils/ensure-function-metadata.d.ts +1 -1
  45. package/dist/utils/extract-services.d.ts +1 -1
  46. package/dist/utils/filter-inspector-state.d.ts +1 -1
  47. package/dist/utils/filter-utils.d.ts +2 -2
  48. package/dist/utils/get-files-and-methods.d.ts +1 -1
  49. package/dist/utils/middleware.d.ts +2 -2
  50. package/dist/utils/permissions.d.ts +2 -2
  51. package/dist/utils/post-process.d.ts +4 -3
  52. package/dist/utils/post-process.js +24 -8
  53. package/dist/utils/resolve-addon-package.d.ts +16 -0
  54. package/dist/utils/{resolve-external-package.js → resolve-addon-package.js} +8 -8
  55. package/dist/utils/schema-generator.d.ts +2 -2
  56. package/dist/utils/serialize-inspector-state.d.ts +17 -3
  57. package/dist/utils/serialize-inspector-state.js +14 -2
  58. package/dist/utils/validate-auth-sessionless.d.ts +3 -0
  59. package/dist/utils/validate-auth-sessionless.js +14 -0
  60. package/dist/utils/workflow/dsl/extract-dsl-workflow.d.ts +1 -1
  61. package/dist/visit.d.ts +1 -1
  62. package/dist/visit.js +4 -0
  63. package/package.json +3 -3
  64. package/src/add/add-ai-agent.ts +2 -2
  65. package/src/add/add-channel.ts +17 -0
  66. package/src/add/add-cli.ts +53 -15
  67. package/src/add/add-file-extends-core-type.ts +1 -1
  68. package/src/add/add-file-with-config.ts +1 -1
  69. package/src/add/add-file-with-factory.ts +3 -3
  70. package/src/add/add-functions.ts +21 -19
  71. package/src/add/add-gateway.ts +95 -0
  72. package/src/add/add-http-route.ts +19 -2
  73. package/src/add/add-http-routes.ts +2 -1
  74. package/src/add/add-keyed-wiring.ts +1 -1
  75. package/src/add/add-mcp-prompt.ts +1 -1
  76. package/src/add/add-mcp-resource.ts +1 -1
  77. package/src/add/add-queue-worker.ts +1 -1
  78. package/src/add/add-rpc-invocations.ts +11 -11
  79. package/src/add/add-schedule.ts +1 -1
  80. package/src/add/add-secret.ts +1 -1
  81. package/src/add/add-trigger.ts +4 -4
  82. package/src/add/add-wire-addon.ts +80 -0
  83. package/src/add/add-workflow.ts +2 -2
  84. package/src/error-codes.ts +1 -0
  85. package/src/index.ts +1 -0
  86. package/src/inspector.ts +17 -5
  87. package/src/types.ts +38 -20
  88. package/src/utils/compute-required-schemas.ts +1 -2
  89. package/src/utils/contract-hashes.test.ts +30 -5
  90. package/src/utils/contract-hashes.ts +110 -14
  91. package/src/utils/custom-types-generator.ts +1 -1
  92. package/src/utils/detect-schema-vendor.ts +1 -1
  93. package/src/utils/does-type-extend-core-type.ts +1 -1
  94. package/src/utils/ensure-function-metadata.ts +1 -1
  95. package/src/utils/extract-services.ts +1 -1
  96. package/src/utils/filter-inspector-state.test.ts +3 -5
  97. package/src/utils/filter-inspector-state.ts +7 -2
  98. package/src/utils/filter-utils.test.ts +1 -1
  99. package/src/utils/filter-utils.ts +2 -2
  100. package/src/utils/get-files-and-methods.ts +1 -1
  101. package/src/utils/middleware.ts +2 -2
  102. package/src/utils/permissions.ts +2 -2
  103. package/src/utils/post-process.ts +34 -12
  104. package/src/utils/{resolve-external-package.ts → resolve-addon-package.ts} +17 -10
  105. package/src/utils/resolve-versions.test.ts +1 -1
  106. package/src/utils/schema-generator.ts +4 -4
  107. package/src/utils/serialize-inspector-state.ts +35 -5
  108. package/src/utils/validate-auth-sessionless.ts +29 -0
  109. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +2 -2
  110. package/src/visit.ts +9 -1
  111. package/tsconfig.tsbuildinfo +1 -1
  112. package/dist/utils/resolve-external-package.d.ts +0 -12
@@ -1,5 +1,6 @@
1
- import { FunctionsMeta, JSONValue, parseVersionedId } from '@pikku/core'
2
- import { TypesMap } from '../types-map.js'
1
+ import type { FunctionsMeta, JSONValue } from '@pikku/core'
2
+ import { parseVersionedId } from '@pikku/core'
3
+ import type { TypesMap } from '../types-map.js'
3
4
  import { ErrorCode } from '../error-codes.js'
4
5
  import { canonicalJSON, hashString } from './hash.js'
5
6
 
@@ -7,16 +8,34 @@ export type ContractEntry = {
7
8
  functionKey: string
8
9
  version: number
9
10
  contractHash: string
11
+ inputHash: string
12
+ outputHash: string
10
13
  }
11
14
 
12
15
  export type VersionValidateError = {
13
16
  code: ErrorCode
14
17
  message: string
18
+ functionKey?: string
19
+ version?: number
20
+ previousHash?: string
21
+ currentHash?: string
22
+ previousInputHash?: string
23
+ currentInputHash?: string
24
+ previousOutputHash?: string
25
+ currentOutputHash?: string
26
+ nextVersion?: number
27
+ latestVersion?: number
28
+ expectedNextVersion?: number
29
+ }
30
+
31
+ export type VersionHashEntry = {
32
+ inputHash: string
33
+ outputHash: string
15
34
  }
16
35
 
17
36
  export type VersionManifestEntry = {
18
37
  latest: number
19
- versions: Record<string, string>
38
+ versions: Record<string, string | VersionHashEntry>
20
39
  }
21
40
 
22
41
  export type VersionManifest = {
@@ -24,6 +43,10 @@ export type VersionManifest = {
24
43
  contracts: Record<string, VersionManifestEntry>
25
44
  }
26
45
 
46
+ function isHashEntry(v: string | VersionHashEntry): v is VersionHashEntry {
47
+ return typeof v === 'object'
48
+ }
49
+
27
50
  export function createEmptyManifest(): VersionManifest {
28
51
  return {
29
52
  manifestVersion: 1,
@@ -36,7 +59,7 @@ export function serializeManifest(manifest: VersionManifest): string {
36
59
 
37
60
  for (const key of Object.keys(manifest.contracts).sort()) {
38
61
  const entry = manifest.contracts[key]
39
- const sortedVersions: Record<string, string> = {}
62
+ const sortedVersions: Record<string, string | VersionHashEntry> = {}
40
63
  const numericKeys = Object.keys(entry.versions)
41
64
  .map(Number)
42
65
  .sort((a, b) => a - b)
@@ -65,6 +88,14 @@ export function computeContractHash(data: {
65
88
  return hashString(canonicalJSON(data), 16)
66
89
  }
67
90
 
91
+ function computeInputHash(functionKey: string, inputSchema: unknown): string {
92
+ return hashString(canonicalJSON({ functionKey, inputSchema }), 8)
93
+ }
94
+
95
+ function computeOutputHash(functionKey: string, outputSchema: unknown): string {
96
+ return hashString(canonicalJSON({ functionKey, outputSchema }), 8)
97
+ }
98
+
68
99
  function resolveSchema(
69
100
  typeNames: string[] | null | undefined,
70
101
  allSchemas: Record<string, JSONValue>,
@@ -120,7 +151,16 @@ export function buildCurrentContracts(
120
151
  inputSchema,
121
152
  outputSchema,
122
153
  })
123
- result.set(funcId, { functionKey, version, contractHash })
154
+ const inputHash = computeInputHash(functionKey, inputSchema)
155
+ const outputHash = computeOutputHash(functionKey, outputSchema)
156
+
157
+ result.set(funcId, {
158
+ functionKey,
159
+ version,
160
+ contractHash,
161
+ inputHash,
162
+ outputHash,
163
+ })
124
164
  }
125
165
 
126
166
  return result
@@ -137,6 +177,8 @@ export function computeContractHashes(
137
177
  const meta = functionsMeta[funcId]
138
178
  if (meta) {
139
179
  meta.contractHash = entry.contractHash
180
+ meta.inputHash = entry.inputHash
181
+ meta.outputHash = entry.outputHash
140
182
  }
141
183
  }
142
184
 
@@ -155,6 +197,19 @@ function groupByFunctionKey(
155
197
  return grouped
156
198
  }
157
199
 
200
+ function entryChanged(
201
+ existing: string | VersionHashEntry,
202
+ current: ContractEntry
203
+ ): boolean {
204
+ if (isHashEntry(existing)) {
205
+ return (
206
+ existing.inputHash !== current.inputHash ||
207
+ existing.outputHash !== current.outputHash
208
+ )
209
+ }
210
+ return existing !== current.contractHash
211
+ }
212
+
158
213
  export function validateContracts(
159
214
  manifest: VersionManifest,
160
215
  currentContracts: Map<string, ContractEntry>
@@ -169,15 +224,32 @@ export function validateContracts(
169
224
  continue
170
225
  }
171
226
 
172
- for (const { version, contractHash } of entries) {
173
- const existingHash = manifestEntry.versions[String(version)]
227
+ for (const current of entries) {
228
+ const { version, contractHash, inputHash, outputHash } = current
229
+ const existingEntry = manifestEntry.versions[String(version)]
174
230
 
175
- if (existingHash !== undefined) {
176
- if (existingHash !== contractHash) {
231
+ if (existingEntry !== undefined) {
232
+ if (entryChanged(existingEntry, current)) {
177
233
  reportedKeys.add(`${functionKey}@${version}`)
234
+ const prevInputHash = isHashEntry(existingEntry)
235
+ ? existingEntry.inputHash
236
+ : existingEntry
237
+ const prevOutputHash = isHashEntry(existingEntry)
238
+ ? existingEntry.outputHash
239
+ : existingEntry
178
240
  errors.push({
179
241
  code: ErrorCode.FUNCTION_VERSION_MODIFIED,
180
- message: `Contract for ${functionKey}@v${version} has changed (recorded: ${existingHash}, current: ${contractHash}). Existing versions are immutable.`,
242
+ message: `Contract for ${functionKey}@v${version} has changed (recorded: ${isHashEntry(existingEntry) ? `${existingEntry.inputHash}/${existingEntry.outputHash}` : existingEntry}, current: ${contractHash}). Existing versions are immutable.`,
243
+ functionKey,
244
+ version,
245
+ previousHash: isHashEntry(existingEntry)
246
+ ? existingEntry.inputHash
247
+ : existingEntry,
248
+ currentHash: contractHash,
249
+ previousInputHash: prevInputHash,
250
+ currentInputHash: inputHash,
251
+ previousOutputHash: prevOutputHash,
252
+ currentOutputHash: outputHash,
181
253
  })
182
254
  }
183
255
  } else {
@@ -185,11 +257,18 @@ export function validateContracts(
185
257
  errors.push({
186
258
  code: ErrorCode.VERSION_REGRESSION_OR_CONFLICT,
187
259
  message: `Version ${version} for ${functionKey} is <= latest (${manifestEntry.latest}) but not recorded. Possible merge conflict.`,
260
+ functionKey,
261
+ version,
262
+ latestVersion: manifestEntry.latest,
188
263
  })
189
264
  } else if (version > manifestEntry.latest + 1) {
190
265
  errors.push({
191
266
  code: ErrorCode.VERSION_GAP_NOT_ALLOWED,
192
267
  message: `Version ${version} for ${functionKey} skips versions. Latest is ${manifestEntry.latest}, next must be ${manifestEntry.latest + 1}.`,
268
+ functionKey,
269
+ version,
270
+ latestVersion: manifestEntry.latest,
271
+ expectedNextVersion: manifestEntry.latest + 1,
193
272
  })
194
273
  }
195
274
  }
@@ -199,7 +278,7 @@ export function validateContracts(
199
278
  for (const [functionKey, manifestEntry] of Object.entries(
200
279
  manifest.contracts
201
280
  )) {
202
- const latestHash = manifestEntry.versions[String(manifestEntry.latest)]
281
+ const latestEntry = manifestEntry.versions[String(manifestEntry.latest)]
203
282
  const currentEntries = grouped.get(functionKey)
204
283
  if (!currentEntries) {
205
284
  continue
@@ -210,12 +289,24 @@ export function validateContracts(
210
289
  )
211
290
  if (
212
291
  currentLatest &&
213
- currentLatest.contractHash !== latestHash &&
292
+ entryChanged(latestEntry, currentLatest) &&
214
293
  !reportedKeys.has(`${functionKey}@${manifestEntry.latest}`)
215
294
  ) {
295
+ const prevInputHash = isHashEntry(latestEntry)
296
+ ? latestEntry.inputHash
297
+ : undefined
298
+ const prevOutputHash = isHashEntry(latestEntry)
299
+ ? latestEntry.outputHash
300
+ : undefined
216
301
  errors.push({
217
302
  code: ErrorCode.CONTRACT_CHANGED_REQUIRES_BUMP,
218
303
  message: `Contract for ${functionKey} changed. Set \`version: ${manifestEntry.latest + 1}\` on the function or run 'pikku versions update'.`,
304
+ functionKey,
305
+ previousInputHash: prevInputHash,
306
+ currentInputHash: currentLatest.inputHash,
307
+ previousOutputHash: prevOutputHash,
308
+ currentOutputHash: currentLatest.outputHash,
309
+ nextVersion: manifestEntry.latest + 1,
219
310
  })
220
311
  }
221
312
  }
@@ -232,6 +323,9 @@ export function validateContracts(
232
323
  errors.push({
233
324
  code: ErrorCode.MANIFEST_INTEGRITY_ERROR,
234
325
  message: `Manifest integrity error for ${functionKey}: latest field (${manifestEntry.latest}) inconsistent with version keys (max: ${maxVersion}).`,
326
+ functionKey,
327
+ latestVersion: manifestEntry.latest,
328
+ expectedNextVersion: maxVersion,
235
329
  })
236
330
  }
237
331
  }
@@ -256,8 +350,8 @@ export function updateManifest(
256
350
  }
257
351
 
258
352
  const entry = manifest.contracts[functionKey]
259
- for (const { version, contractHash } of entries) {
260
- entry.versions[String(version)] = contractHash
353
+ for (const { version, inputHash, outputHash } of entries) {
354
+ entry.versions[String(version)] = { inputHash, outputHash }
261
355
  entry.latest = Math.max(entry.latest, version)
262
356
  }
263
357
  }
@@ -283,6 +377,8 @@ export function extractContractsFromMeta(
283
377
  functionKey,
284
378
  version,
285
379
  contractHash: meta.contractHash,
380
+ inputHash: meta.inputHash ?? '',
381
+ outputHash: meta.outputHash ?? '',
286
382
  })
287
383
  }
288
384
 
@@ -1,4 +1,4 @@
1
- import { TypesMap } from '../types-map.js'
1
+ import type { TypesMap } from '../types-map.js'
2
2
 
3
3
  /**
4
4
  * NOTE: Code generation normally belongs in @pikku/cli, not the inspector.
@@ -1,4 +1,4 @@
1
- import * as ts from 'typescript'
1
+ import type * as ts from 'typescript'
2
2
  import type { SchemaVendor, InspectorLogger } from '../types.js'
3
3
  import { ErrorCode } from '../error-codes.js'
4
4
 
@@ -1,4 +1,4 @@
1
- import * as ts from 'typescript'
1
+ import type * as ts from 'typescript'
2
2
 
3
3
  export const doesTypeExtendsCore = (
4
4
  type: ts.Type,
@@ -1,5 +1,5 @@
1
1
  import * as ts from 'typescript'
2
- import { InspectorState } from '../types.js'
2
+ import type { InspectorState } from '../types.js'
3
3
  import { funcIdToTypeName } from './extract-function-name.js'
4
4
  import { getCommonWireMetaData } from './get-property-value.js'
5
5
  import { resolveMiddleware } from './middleware.js'
@@ -1,5 +1,5 @@
1
1
  import * as ts from 'typescript'
2
- import { FunctionServicesMeta, FunctionWiresMeta } from '@pikku/core'
2
+ import type { FunctionServicesMeta, FunctionWiresMeta } from '@pikku/core'
3
3
 
4
4
  /**
5
5
  * Extract services from a function's first parameter destructuring pattern
@@ -1,11 +1,9 @@
1
1
  import { test, describe } from 'node:test'
2
2
  import { strict as assert } from 'node:assert'
3
3
  import { filterInspectorState } from './filter-inspector-state.js'
4
- import { InspectorState, InspectorFilters } from '../types.js'
5
- import {
6
- deserializeInspectorState,
7
- SerializableInspectorState,
8
- } from './serialize-inspector-state.js'
4
+ import type { InspectorState, InspectorFilters } from '../types.js'
5
+ import type { SerializableInspectorState } from './serialize-inspector-state.js'
6
+ import { deserializeInspectorState } from './serialize-inspector-state.js'
9
7
  import { readFileSync } from 'node:fs'
10
8
  import { fileURLToPath } from 'node:url'
11
9
  import { dirname, join } from 'node:path'
@@ -1,5 +1,10 @@
1
- import { InspectorState, InspectorFilters, InspectorLogger } from '../types.js'
2
- import { PikkuWiringTypes, parseVersionedId } from '@pikku/core'
1
+ import type {
2
+ InspectorState,
3
+ InspectorFilters,
4
+ InspectorLogger,
5
+ } from '../types.js'
6
+ import type { PikkuWiringTypes } from '@pikku/core'
7
+ import { parseVersionedId } from '@pikku/core'
3
8
  import { aggregateRequiredServices } from './post-process.js'
4
9
 
5
10
  // Module-level Set to track warned groups across multiple filter calls
@@ -1,6 +1,6 @@
1
1
  import { test, describe } from 'node:test'
2
2
  import { strict as assert } from 'node:assert'
3
- import { InspectorFilters } from '../types'
3
+ import type { InspectorFilters } from '../types'
4
4
  import { matchesFilters, matchesWildcard } from './filter-utils'
5
5
 
6
6
  describe('matchesFilters', () => {
@@ -1,5 +1,5 @@
1
- import { InspectorFilters, InspectorLogger } from '../types.js'
2
- import { PikkuWiringTypes } from '@pikku/core'
1
+ import type { InspectorFilters, InspectorLogger } from '../types.js'
2
+ import type { PikkuWiringTypes } from '@pikku/core'
3
3
 
4
4
  /**
5
5
  * Match a value against a pattern with wildcard support
@@ -1,4 +1,4 @@
1
- import {
1
+ import type {
2
2
  PathToNameAndType,
3
3
  InspectorState,
4
4
  InspectorOptions,
@@ -1,7 +1,7 @@
1
1
  import * as ts from 'typescript'
2
- import { MiddlewareMetadata } from '@pikku/core'
2
+ import type { MiddlewareMetadata } from '@pikku/core'
3
3
  import { extractFunctionName } from './extract-function-name.js'
4
- import { InspectorState } from '../types.js'
4
+ import type { InspectorState } from '../types.js'
5
5
 
6
6
  export interface MiddlewareRef {
7
7
  definitionId: string
@@ -1,7 +1,7 @@
1
1
  import * as ts from 'typescript'
2
- import { PermissionMetadata } from '@pikku/core'
2
+ import type { PermissionMetadata } from '@pikku/core'
3
3
  import { extractFunctionName } from './extract-function-name.js'
4
- import { InspectorState } from '../types.js'
4
+ import type { InspectorState } from '../types.js'
5
5
 
6
6
  /**
7
7
  * Extract permission pikkuFuncIds from an expression (array or object literal)
@@ -1,13 +1,12 @@
1
- import {
1
+ import type {
2
2
  InspectorState,
3
3
  InspectorLogger,
4
4
  InspectorOptions,
5
5
  InspectorModelConfig,
6
- ExternalPackageConfig,
7
6
  MiddlewareGroupMeta,
8
7
  InspectorDiagnostic,
9
8
  } from '../types.js'
10
- import {
9
+ import type {
11
10
  FunctionServicesMeta,
12
11
  MiddlewareMetadata,
13
12
  PermissionMetadata,
@@ -226,22 +225,46 @@ export function aggregateRequiredServices(
226
225
 
227
226
  export function validateSecretOverrides(
228
227
  logger: InspectorLogger,
229
- state: InspectorState | Omit<InspectorState, 'typesLookup'>,
230
- externalPackages?: Record<string, ExternalPackageConfig>
228
+ state: InspectorState | Omit<InspectorState, 'typesLookup'>
231
229
  ): void {
232
- if (!externalPackages) return
230
+ const { wireAddonDeclarations } = state.rpc
231
+ if (!wireAddonDeclarations || wireAddonDeclarations.size === 0) return
233
232
 
234
233
  const secretNames = new Set(state.secrets.definitions.map((d) => d.name))
235
234
 
236
- for (const [namespace, pkgConfig] of Object.entries(externalPackages)) {
237
- if (!pkgConfig.secretOverrides) continue
235
+ for (const [namespace, addonDecl] of wireAddonDeclarations.entries()) {
236
+ if (!addonDecl.secretOverrides) continue
238
237
 
239
- for (const secretKey of Object.keys(pkgConfig.secretOverrides)) {
238
+ for (const secretKey of Object.keys(addonDecl.secretOverrides)) {
240
239
  if (!secretNames.has(secretKey)) {
241
240
  const availableSecrets = Array.from(secretNames)
242
241
  logger.critical(
243
242
  ErrorCode.INVALID_VALUE,
244
- `Secret override '${secretKey}' in external package '${namespace}' (${pkgConfig.package}) does not exist. Available secrets: ${availableSecrets.join(', ') || 'none'}`
243
+ `Secret override '${secretKey}' in addon '${namespace}' (${addonDecl.package}) does not exist. Available secrets: ${availableSecrets.join(', ') || 'none'}`
244
+ )
245
+ }
246
+ }
247
+ }
248
+ }
249
+
250
+ export function validateVariableOverrides(
251
+ logger: InspectorLogger,
252
+ state: InspectorState | Omit<InspectorState, 'typesLookup'>
253
+ ): void {
254
+ const { wireAddonDeclarations } = state.rpc
255
+ if (!wireAddonDeclarations || wireAddonDeclarations.size === 0) return
256
+
257
+ const variableNames = new Set(state.variables.definitions.map((d) => d.name))
258
+
259
+ for (const [namespace, addonDecl] of wireAddonDeclarations.entries()) {
260
+ if (!addonDecl.variableOverrides) continue
261
+
262
+ for (const variableKey of Object.keys(addonDecl.variableOverrides)) {
263
+ if (!variableNames.has(variableKey)) {
264
+ const availableVariables = Array.from(variableNames)
265
+ logger.critical(
266
+ ErrorCode.INVALID_VALUE,
267
+ `Variable override '${variableKey}' in addon '${namespace}' (${addonDecl.package}) does not exist. Available variables: ${availableVariables.join(', ') || 'none'}`
245
268
  )
246
269
  }
247
270
  }
@@ -363,7 +386,7 @@ export function computeRequiredSchemas(
363
386
 
364
387
  state.requiredSchemas = new Set<string>([
365
388
  ...Object.values(functions.meta)
366
- .map(({ inputs, outputs }) => {
389
+ .flatMap(({ inputs, outputs }) => {
367
390
  const types: (string | undefined)[] = []
368
391
  if (inputs?.[0]) {
369
392
  try {
@@ -381,7 +404,6 @@ export function computeRequiredSchemas(
381
404
  }
382
405
  return types
383
406
  })
384
- .flat()
385
407
  .filter((s): s is string => !!s && !PRIMITIVE_TYPES.has(s)),
386
408
  ...functions.typesMap.customTypes.keys(),
387
409
  ...(schemasFromTypes || []),
@@ -1,21 +1,28 @@
1
1
  import * as ts from 'typescript'
2
- import { ExternalPackageConfig } from '../types.js'
3
2
 
4
3
  /**
5
- * Resolve the external package name from an imported identifier.
4
+ * Resolve the addon package name from an imported identifier.
6
5
  * Checks if the identifier's import module specifier matches any
7
- * configured external package.
6
+ * configured addon package.
8
7
  *
9
8
  * This is a general utility — any wire handler that processes a `func`
10
9
  * property can use it to detect when the function comes from an
11
- * external package.
10
+ * addon package.
12
11
  */
13
- export const resolveExternalPackageName = (
12
+ export const resolveAddonName = (
14
13
  identifier: ts.Identifier,
15
14
  checker: ts.TypeChecker,
16
- externalPackages?: Record<string, ExternalPackageConfig>
15
+ wireAddonDeclarations?: Map<
16
+ string,
17
+ {
18
+ package: string
19
+ rpcEndpoint?: string
20
+ secretOverrides?: Record<string, string>
21
+ variableOverrides?: Record<string, string>
22
+ }
23
+ >
17
24
  ): string | null => {
18
- if (!externalPackages || Object.keys(externalPackages).length === 0) {
25
+ if (!wireAddonDeclarations || wireAddonDeclarations.size === 0) {
19
26
  return null
20
27
  }
21
28
 
@@ -32,9 +39,9 @@ export const resolveExternalPackageName = (
32
39
 
33
40
  const moduleSpecifier = importDecl.moduleSpecifier.text
34
41
 
35
- for (const config of Object.values(externalPackages)) {
36
- if (config.package === moduleSpecifier) {
37
- return config.package
42
+ for (const addonDecl of wireAddonDeclarations.values()) {
43
+ if (addonDecl.package === moduleSpecifier) {
44
+ return addonDecl.package
38
45
  }
39
46
  }
40
47
 
@@ -22,7 +22,7 @@ function makeState(
22
22
  exposedMeta: {},
23
23
  exposedFiles: new Map(),
24
24
  invokedFunctions: new Set(),
25
- usedExternalPackages: new Set(),
25
+ usedAddons: new Set(),
26
26
  },
27
27
  serviceAggregation: {
28
28
  usedFunctions: new Set(),
@@ -4,11 +4,11 @@ import { createGenerator, RootlessError } from 'ts-json-schema-generator'
4
4
  import { tsImport } from 'tsx/esm/api'
5
5
  import * as z from 'zod'
6
6
  import { zodToTs, createAuxiliaryTypeStore } from 'zod-to-ts'
7
- import { FunctionsMeta, JSONValue } from '@pikku/core'
8
- import { HTTPWiringsMeta } from '@pikku/core/http'
9
- import { TypesMap } from '../types-map.js'
7
+ import type { FunctionsMeta, JSONValue } from '@pikku/core'
8
+ import type { HTTPWiringsMeta } from '@pikku/core/http'
9
+ import type { TypesMap } from '../types-map.js'
10
10
  import { ErrorCode } from '../error-codes.js'
11
- import { InspectorLogger, InspectorState, SchemaRef } from '../types.js'
11
+ import type { InspectorLogger, InspectorState, SchemaRef } from '../types.js'
12
12
  import { generateCustomTypes } from './custom-types-generator.js'
13
13
 
14
14
  const PRIMITIVE_TYPES = new Set([
@@ -1,5 +1,5 @@
1
- import { JSONValue } from '@pikku/core'
2
- import { InspectorDiagnostic, InspectorState } from '../types.js'
1
+ import type { JSONValue } from '@pikku/core'
2
+ import type { InspectorDiagnostic, InspectorState } from '../types.js'
3
3
  import { TypesMap } from '../types-map.js'
4
4
 
5
5
  /**
@@ -116,6 +116,10 @@ export interface SerializableInspectorState {
116
116
  files: string[]
117
117
  meta: InspectorState['channels']['meta']
118
118
  }
119
+ gateways: {
120
+ meta: InspectorState['gateways']['meta']
121
+ files: string[]
122
+ }
119
123
  triggers: {
120
124
  meta: InspectorState['triggers']['meta']
121
125
  sourceMeta: InspectorState['triggers']['sourceMeta']
@@ -142,7 +146,19 @@ export interface SerializableInspectorState {
142
146
  exposedMeta: InspectorState['rpc']['exposedMeta']
143
147
  exposedFiles: Array<[string, { path: string; exportedName: string }]>
144
148
  invokedFunctions: string[]
145
- usedExternalPackages: string[]
149
+ usedAddons: string[]
150
+ wireAddonDeclarations: Array<
151
+ [
152
+ string,
153
+ {
154
+ package: string
155
+ rpcEndpoint?: string
156
+ secretOverrides?: Record<string, string>
157
+ variableOverrides?: Record<string, string>
158
+ },
159
+ ]
160
+ >
161
+ wireAddonFiles: string[]
146
162
  }
147
163
  mcpEndpoints: {
148
164
  resourcesMeta: InspectorState['mcpEndpoints']['resourcesMeta']
@@ -303,6 +319,10 @@ export function serializeInspectorState(
303
319
  files: Array.from(state.channels.files),
304
320
  meta: state.channels.meta,
305
321
  },
322
+ gateways: {
323
+ meta: state.gateways.meta,
324
+ files: Array.from(state.gateways.files),
325
+ },
306
326
  triggers: {
307
327
  meta: state.triggers.meta,
308
328
  sourceMeta: state.triggers.sourceMeta,
@@ -329,7 +349,11 @@ export function serializeInspectorState(
329
349
  exposedMeta: state.rpc.exposedMeta,
330
350
  exposedFiles: Array.from(state.rpc.exposedFiles.entries()),
331
351
  invokedFunctions: Array.from(state.rpc.invokedFunctions),
332
- usedExternalPackages: Array.from(state.rpc.usedExternalPackages),
352
+ usedAddons: Array.from(state.rpc.usedAddons),
353
+ wireAddonDeclarations: Array.from(
354
+ state.rpc.wireAddonDeclarations.entries()
355
+ ),
356
+ wireAddonFiles: Array.from(state.rpc.wireAddonFiles),
333
357
  },
334
358
  mcpEndpoints: {
335
359
  resourcesMeta: state.mcpEndpoints.resourcesMeta,
@@ -463,6 +487,10 @@ export function deserializeInspectorState(
463
487
  files: new Set(data.channels.files),
464
488
  meta: data.channels.meta,
465
489
  },
490
+ gateways: {
491
+ meta: data.gateways?.meta ?? {},
492
+ files: new Set(data.gateways?.files ?? []),
493
+ },
466
494
  triggers: {
467
495
  meta: data.triggers?.meta ?? {},
468
496
  sourceMeta: data.triggers?.sourceMeta ?? {},
@@ -489,7 +517,9 @@ export function deserializeInspectorState(
489
517
  exposedMeta: data.rpc.exposedMeta,
490
518
  exposedFiles: new Map(data.rpc.exposedFiles),
491
519
  invokedFunctions: new Set(data.rpc.invokedFunctions),
492
- usedExternalPackages: new Set(data.rpc.usedExternalPackages || []),
520
+ usedAddons: new Set(data.rpc.usedAddons || []),
521
+ wireAddonDeclarations: new Map(data.rpc.wireAddonDeclarations || []),
522
+ wireAddonFiles: new Set(data.rpc.wireAddonFiles || []),
493
523
  },
494
524
  mcpEndpoints: {
495
525
  resourcesMeta: data.mcpEndpoints.resourcesMeta,
@@ -0,0 +1,29 @@
1
+ import * as ts from 'typescript'
2
+ import { getPropertyValue } from './get-property-value.js'
3
+ import { ErrorCode } from '../error-codes.js'
4
+ import type { InspectorLogger, InspectorState } from '../types.js'
5
+
6
+ export function validateAuthSessionless(
7
+ logger: InspectorLogger,
8
+ obj: ts.ObjectLiteralExpression,
9
+ state: InspectorState,
10
+ funcName: string,
11
+ wireDescription: string,
12
+ inheritedAuth?: boolean
13
+ ): boolean {
14
+ const fnMeta = state.functions.meta[funcName]
15
+ if (!fnMeta) return true
16
+
17
+ const routeAuth = getPropertyValue(obj, 'auth')
18
+ const resolvedAuth =
19
+ routeAuth === true || routeAuth === false ? routeAuth : inheritedAuth
20
+ if (resolvedAuth === false && fnMeta.sessionless === false) {
21
+ logger.critical(
22
+ ErrorCode.AUTH_DISABLED_REQUIRES_SESSIONLESS,
23
+ `${wireDescription} has auth disabled but function '${funcName}' uses pikkuFunc (requires session). Use pikkuSessionlessFunc instead.`
24
+ )
25
+ return false
26
+ }
27
+
28
+ return true
29
+ }
@@ -1,5 +1,5 @@
1
1
  import * as ts from 'typescript'
2
- import {
2
+ import type {
3
3
  WorkflowStepMeta,
4
4
  RpcStepMeta,
5
5
  BranchStepMeta,
@@ -33,11 +33,11 @@ import {
33
33
  isArrayType,
34
34
  getSourceText,
35
35
  } from './patterns.js'
36
+ import type { ValidationError } from './validation.js'
36
37
  import {
37
38
  validateNoDisallowedPatterns,
38
39
  validateAwaitedCalls,
39
40
  formatValidationErrors,
40
- ValidationError,
41
41
  } from './validation.js'
42
42
  import {
43
43
  extractStringLiteral,