@pikku/inspector 0.12.75 → 0.12.81

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 (219) hide show
  1. package/CHANGELOG.md +330 -0
  2. package/dist/add/add-analytics.d.ts +9 -0
  3. package/dist/add/add-analytics.js +178 -0
  4. package/dist/add/add-auth.js +0 -54
  5. package/dist/add/add-feature-flag.d.ts +2 -0
  6. package/dist/add/add-feature-flag.js +162 -0
  7. package/dist/add/add-wire-addon.js +1 -0
  8. package/dist/add/add-wire-remote-addon.js +1 -0
  9. package/dist/index.d.ts +3 -1
  10. package/dist/index.js +3 -1
  11. package/dist/inspector.js +4 -0
  12. package/dist/surface/collect-surface.d.ts +58 -0
  13. package/dist/surface/collect-surface.js +613 -0
  14. package/dist/types.d.ts +20 -11
  15. package/dist/utils/filter-inspector-state.js +14 -0
  16. package/dist/utils/post-process.js +15 -0
  17. package/dist/utils/read-module-specifiers.d.ts +8 -0
  18. package/dist/utils/read-module-specifiers.js +23 -0
  19. package/dist/utils/read-tsconfig-out-dir.d.ts +5 -0
  20. package/dist/utils/read-tsconfig-out-dir.js +12 -0
  21. package/dist/utils/resolve-deploy-target.d.ts +7 -0
  22. package/dist/utils/resolve-deploy-target.js +15 -7
  23. package/dist/utils/serialize-inspector-state.d.ts +5 -0
  24. package/dist/utils/serialize-inspector-state.js +8 -0
  25. package/dist/visit.js +4 -0
  26. package/package.json +16 -6
  27. package/run-tests.sh +21 -6
  28. package/src/add/add-addon-bans.ts +0 -85
  29. package/src/add/add-agent-scorer.test.ts +0 -197
  30. package/src/add/add-agent-scorer.ts +0 -83
  31. package/src/add/add-agent.test.ts +0 -87
  32. package/src/add/add-agent.ts +0 -638
  33. package/src/add/add-approval-description.ts +0 -76
  34. package/src/add/add-auth.test.ts +0 -791
  35. package/src/add/add-auth.ts +0 -424
  36. package/src/add/add-channel.ts +0 -778
  37. package/src/add/add-cli-renderers.test.ts +0 -74
  38. package/src/add/add-cli.ts +0 -1006
  39. package/src/add/add-credential.ts +0 -186
  40. package/src/add/add-feature.test.ts +0 -306
  41. package/src/add/add-feature.ts +0 -253
  42. package/src/add/add-file-extends-core-type.ts +0 -66
  43. package/src/add/add-file-with-config.ts +0 -45
  44. package/src/add/add-file-with-factory.test.ts +0 -134
  45. package/src/add/add-file-with-factory.ts +0 -288
  46. package/src/add/add-functions.test.ts +0 -743
  47. package/src/add/add-functions.ts +0 -1548
  48. package/src/add/add-gateway.ts +0 -143
  49. package/src/add/add-http-route-ref.test.ts +0 -149
  50. package/src/add/add-http-route.ts +0 -530
  51. package/src/add/add-http-routes.ts +0 -471
  52. package/src/add/add-keyed-wiring.ts +0 -180
  53. package/src/add/add-mcp-prompt.ts +0 -157
  54. package/src/add/add-mcp-resource.ts +0 -175
  55. package/src/add/add-middleware.test.ts +0 -196
  56. package/src/add/add-middleware.ts +0 -879
  57. package/src/add/add-permission-auth.test.ts +0 -59
  58. package/src/add/add-permission.ts +0 -311
  59. package/src/add/add-personas.test.ts +0 -406
  60. package/src/add/add-personas.ts +0 -287
  61. package/src/add/add-queue-worker.ts +0 -117
  62. package/src/add/add-rpc-invocations.test.ts +0 -287
  63. package/src/add/add-rpc-invocations.ts +0 -221
  64. package/src/add/add-scenario-step.test.ts +0 -509
  65. package/src/add/add-schedule.ts +0 -120
  66. package/src/add/add-scope.test.ts +0 -419
  67. package/src/add/add-scope.ts +0 -221
  68. package/src/add/add-secret.ts +0 -10
  69. package/src/add/add-subject-scenario-steps.test.ts +0 -161
  70. package/src/add/add-system-role.test.ts +0 -297
  71. package/src/add/add-system-role.ts +0 -162
  72. package/src/add/add-trigger.ts +0 -165
  73. package/src/add/add-variable.ts +0 -10
  74. package/src/add/add-wire-addon.test.ts +0 -185
  75. package/src/add/add-wire-addon.ts +0 -146
  76. package/src/add/add-wire-remote-addon.ts +0 -80
  77. package/src/add/add-workflow-edge-cases.test.ts +0 -201
  78. package/src/add/add-workflow-fanout.test.ts +0 -166
  79. package/src/add/add-workflow-graph-foreach.test.ts +0 -174
  80. package/src/add/add-workflow-graph-input.test.ts +0 -94
  81. package/src/add/add-workflow-graph-notes.test.ts +0 -74
  82. package/src/add/add-workflow-graph.ts +0 -644
  83. package/src/add/add-workflow-on-error.test.ts +0 -118
  84. package/src/add/add-workflow-return-switch.test.ts +0 -121
  85. package/src/add/add-workflow-silent-drops.test.ts +0 -261
  86. package/src/add/add-workflow-sleep-expression.test.ts +0 -96
  87. package/src/add/add-workflow.test.ts +0 -155
  88. package/src/add/add-workflow.ts +0 -482
  89. package/src/add/addon-bans.test.ts +0 -121
  90. package/src/add/addon-contracts.test.ts +0 -221
  91. package/src/add/allow-escape-hatches.test.ts +0 -144
  92. package/src/add/audit-meta.test.ts +0 -75
  93. package/src/add/classified-sink-check.test.ts +0 -347
  94. package/src/add/config-value-metadata.test.ts +0 -263
  95. package/src/add/dynamic-import-check.test.ts +0 -119
  96. package/src/add/expect-eventually-scenario-only.test.ts +0 -108
  97. package/src/add/function-body-span.test.ts +0 -96
  98. package/src/add/inline-wiring-function.test.ts +0 -104
  99. package/src/add/pii-check.test.ts +0 -258
  100. package/src/add/rpc-type-cast.test.ts +0 -123
  101. package/src/add/scenario-instrumentation.test.ts +0 -89
  102. package/src/add/scenario-instrumentation.ts +0 -42
  103. package/src/add/scenario-marker.test.ts +0 -89
  104. package/src/add/scenario-not-extractable.test.ts +0 -159
  105. package/src/add/scenario-services.test.ts +0 -103
  106. package/src/add/scenario-skip.test.ts +0 -83
  107. package/src/add/validate-workflow-graph-addons.test.ts +0 -102
  108. package/src/add/wire-name-literal.test.ts +0 -117
  109. package/src/error-codes-emitted.test.ts +0 -76
  110. package/src/error-codes.ts +0 -145
  111. package/src/index.ts +0 -66
  112. package/src/inspector.ts +0 -575
  113. package/src/schema-generator.ts +0 -1
  114. package/src/source-file-cache.ts +0 -73
  115. package/src/tsconfig-paths.test.ts +0 -90
  116. package/src/types-map.ts +0 -141
  117. package/src/types.ts +0 -775
  118. package/src/utils/annotate-http-route-auth.test.ts +0 -161
  119. package/src/utils/annotate-http-route-auth.ts +0 -53
  120. package/src/utils/check-classified-sinks.ts +0 -215
  121. package/src/utils/check-pii-output.ts +0 -120
  122. package/src/utils/compute-diagnostics.test.ts +0 -106
  123. package/src/utils/compute-required-schemas.ts +0 -48
  124. package/src/utils/contract-hashes.test.ts +0 -553
  125. package/src/utils/contract-hashes.ts +0 -387
  126. package/src/utils/custom-types-generator.test.ts +0 -124
  127. package/src/utils/custom-types-generator.ts +0 -150
  128. package/src/utils/detect-schema-vendor.test.ts +0 -123
  129. package/src/utils/detect-schema-vendor.ts +0 -120
  130. package/src/utils/does-type-extend-core-type.ts +0 -53
  131. package/src/utils/ensure-function-metadata.ts +0 -385
  132. package/src/utils/extract-function-name.ts +0 -612
  133. package/src/utils/extract-function-node.ts +0 -70
  134. package/src/utils/extract-node-value.test.ts +0 -117
  135. package/src/utils/extract-node-value.ts +0 -172
  136. package/src/utils/extract-secret-usage.test.ts +0 -81
  137. package/src/utils/extract-secret-usage.ts +0 -73
  138. package/src/utils/extract-services.ts +0 -70
  139. package/src/utils/extract-surface-usage.test.ts +0 -147
  140. package/src/utils/extract-surface-usage.ts +0 -121
  141. package/src/utils/filter-inspector-state.test.ts +0 -2232
  142. package/src/utils/filter-inspector-state.ts +0 -1298
  143. package/src/utils/filter-utils.test.ts +0 -843
  144. package/src/utils/filter-utils.ts +0 -161
  145. package/src/utils/find-root-dir.ts +0 -68
  146. package/src/utils/find-zod-transform.test.ts +0 -70
  147. package/src/utils/find-zod-transform.ts +0 -103
  148. package/src/utils/get-exported-variable-name.ts +0 -48
  149. package/src/utils/get-files-and-methods.ts +0 -157
  150. package/src/utils/get-property-value.test.ts +0 -141
  151. package/src/utils/get-property-value.ts +0 -331
  152. package/src/utils/hash.ts +0 -26
  153. package/src/utils/literal-properties.ts +0 -97
  154. package/src/utils/load-addon-functions-meta.test.ts +0 -157
  155. package/src/utils/load-addon-functions-meta.ts +0 -508
  156. package/src/utils/load-addon-scopes.test.ts +0 -138
  157. package/src/utils/middleware.test.ts +0 -204
  158. package/src/utils/middleware.ts +0 -303
  159. package/src/utils/nested-project-filter.test.ts +0 -53
  160. package/src/utils/nested-project-filter.ts +0 -28
  161. package/src/utils/permissions-shorthand.test.ts +0 -79
  162. package/src/utils/permissions.test.ts +0 -125
  163. package/src/utils/permissions.ts +0 -129
  164. package/src/utils/post-process.test.ts +0 -1094
  165. package/src/utils/post-process.ts +0 -1604
  166. package/src/utils/resolve-addon-package.ts +0 -106
  167. package/src/utils/resolve-core-type.test.ts +0 -142
  168. package/src/utils/resolve-core-type.ts +0 -35
  169. package/src/utils/resolve-deploy-target.test.ts +0 -135
  170. package/src/utils/resolve-deploy-target.ts +0 -65
  171. package/src/utils/resolve-function-meta.ts +0 -25
  172. package/src/utils/resolve-function-types.ts +0 -42
  173. package/src/utils/resolve-identifier.ts +0 -46
  174. package/src/utils/resolve-ref-contract.ts +0 -71
  175. package/src/utils/resolve-versions.test.ts +0 -499
  176. package/src/utils/resolve-versions.ts +0 -228
  177. package/src/utils/schema-cache.test.ts +0 -124
  178. package/src/utils/schema-generator.test.ts +0 -272
  179. package/src/utils/schema-generator.ts +0 -780
  180. package/src/utils/secret-alias-services.test.ts +0 -146
  181. package/src/utils/secret-alias-services.ts +0 -58
  182. package/src/utils/serialize-inspector-state.ts +0 -742
  183. package/src/utils/serialize-mcp-json.ts +0 -150
  184. package/src/utils/serialize-middleware-groups-meta.ts +0 -36
  185. package/src/utils/serialize-openapi-json.ts +0 -277
  186. package/src/utils/serialize-permissions-groups-meta.ts +0 -7
  187. package/src/utils/single-declaration.ts +0 -57
  188. package/src/utils/type-utils.ts +0 -145
  189. package/src/utils/validate-auth-sessionless.ts +0 -30
  190. package/src/utils/validate-exposed-functions-gated.test.ts +0 -230
  191. package/src/utils/validate-exposed-functions-gated.ts +0 -103
  192. package/src/utils/validate-tags-resolve-to-middleware.test.ts +0 -108
  193. package/src/utils/validate-tags-resolve-to-middleware.ts +0 -48
  194. package/src/utils/workflow/derive-workflow-plan.test.ts +0 -178
  195. package/src/utils/workflow/derive-workflow-plan.ts +0 -117
  196. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +0 -1465
  197. package/src/utils/workflow/dsl/deserialize-fanout-body.test.ts +0 -71
  198. package/src/utils/workflow/dsl/deserialize-fanout-fidelity.test.ts +0 -99
  199. package/src/utils/workflow/dsl/deserialize-graph-next.test.ts +0 -161
  200. package/src/utils/workflow/dsl/deserialize-roundtrip-bindings.test.ts +0 -121
  201. package/src/utils/workflow/dsl/deserialize-roundtrip-values.test.ts +0 -116
  202. package/src/utils/workflow/dsl/deserialize-roundtrip.test.ts +0 -145
  203. package/src/utils/workflow/dsl/extract-approval-policy.test.ts +0 -71
  204. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +0 -2254
  205. package/src/utils/workflow/dsl/index.ts +0 -11
  206. package/src/utils/workflow/dsl/patterns.ts +0 -440
  207. package/src/utils/workflow/dsl/validation.ts +0 -203
  208. package/src/utils/workflow/graph/convert-dsl-to-graph-skip.test.ts +0 -35
  209. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +0 -543
  210. package/src/utils/workflow/graph/fanout-node-identity.test.ts +0 -105
  211. package/src/utils/workflow/graph/finalize-workflow-wires.ts +0 -310
  212. package/src/utils/workflow/graph/finalize-workflows.test.ts +0 -50
  213. package/src/utils/workflow/graph/finalize-workflows.ts +0 -130
  214. package/src/utils/workflow/graph/index.ts +0 -28
  215. package/src/utils/workflow/graph/serialize-workflow-graph.ts +0 -240
  216. package/src/utils/workflow/graph/workflow-graph.types.ts +0 -281
  217. package/src/visit.ts +0 -181
  218. package/tsconfig.json +0 -19
  219. package/tsconfig.tsbuildinfo +0 -1
@@ -1,106 +0,0 @@
1
- import * as ts from 'typescript'
2
- import { existsSync, readFileSync } from 'fs'
3
- import { dirname, join, parse } from 'path'
4
-
5
- const packageNameCache = new Map<string, string | null>()
6
-
7
- const findPackageNameForFile = (filePath: string): string | null => {
8
- if (packageNameCache.has(filePath)) {
9
- return packageNameCache.get(filePath)!
10
- }
11
- const root = parse(filePath).root
12
- let dir = dirname(filePath)
13
- while (dir && dir !== root) {
14
- const pkgPath = join(dir, 'package.json')
15
- if (existsSync(pkgPath)) {
16
- try {
17
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))
18
- const name = typeof pkg.name === 'string' ? pkg.name : null
19
- packageNameCache.set(filePath, name)
20
- return name
21
- } catch {
22
- packageNameCache.set(filePath, null)
23
- return null
24
- }
25
- }
26
- const parent = dirname(dir)
27
- if (parent === dir) break
28
- dir = parent
29
- }
30
- packageNameCache.set(filePath, null)
31
- return null
32
- }
33
-
34
- /**
35
- * Resolve the addon package name from an imported identifier.
36
- * Checks if the identifier's import module specifier matches any
37
- * configured addon package — and if the import is relative (because
38
- * the identifier resolves to a source file inside the addon package
39
- * itself), walks up to the nearest package.json to obtain the real
40
- * package name.
41
- */
42
- export const resolveAddonName = (
43
- identifier: ts.Identifier,
44
- checker: ts.TypeChecker,
45
- wireAddonDeclarations?: Map<
46
- string,
47
- {
48
- package: string
49
- rpcEndpoint?: string
50
- secretOverrides?: Record<string, string>
51
- variableOverrides?: Record<string, string>
52
- credentialOverrides?: Record<string, string>
53
- }
54
- >
55
- ): string | null => {
56
- if (!wireAddonDeclarations || wireAddonDeclarations.size === 0) {
57
- return null
58
- }
59
-
60
- const sym = checker.getSymbolAtLocation(identifier)
61
- if (!sym) return null
62
-
63
- const decl = sym.declarations?.[0]
64
- if (!decl) return null
65
-
66
- let candidatePackage: string | null = null
67
-
68
- if (ts.isImportSpecifier(decl)) {
69
- // ImportSpecifier -> NamedImports -> ImportClause -> ImportDeclaration
70
- const importDecl = decl.parent?.parent?.parent
71
- if (
72
- importDecl &&
73
- ts.isImportDeclaration(importDecl) &&
74
- ts.isStringLiteral(importDecl.moduleSpecifier)
75
- ) {
76
- candidatePackage = importDecl.moduleSpecifier.text
77
- }
78
- }
79
-
80
- // Bare package import path
81
- if (candidatePackage && !candidatePackage.startsWith('.')) {
82
- for (const addonDecl of wireAddonDeclarations.values()) {
83
- if (
84
- addonDecl.package === candidatePackage ||
85
- candidatePackage.startsWith(`${addonDecl.package}/`)
86
- ) {
87
- return addonDecl.package
88
- }
89
- }
90
- }
91
-
92
- // Fall back to package.json lookup based on the declaration's source file.
93
- // This catches the case where the identifier resolves into an addon
94
- // package's own internal source (relative import inside that package).
95
- const declFile = decl.getSourceFile()?.fileName
96
- if (declFile) {
97
- const pkgName = findPackageNameForFile(declFile)
98
- if (pkgName) {
99
- for (const addonDecl of wireAddonDeclarations.values()) {
100
- if (addonDecl.package === pkgName) return addonDecl.package
101
- }
102
- }
103
- }
104
-
105
- return null
106
- }
@@ -1,142 +0,0 @@
1
- import { describe, test } from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import ts from 'typescript'
4
- import { aggregateRequiredServices } from './post-process.js'
5
- import type { InspectorState, PathToNameAndType } from '../types.js'
6
-
7
- const SOURCE_FILE = '/application-types.ts'
8
-
9
- /**
10
- * Builds the slice of inspector state that service extraction reads: the
11
- * `typesLookup` entry the AST walk records under whatever the project named its
12
- * interface, and the import map that points at that name.
13
- */
14
- const stateWithCoreTypes = (
15
- source: string,
16
- singletonName: string,
17
- wireName: string
18
- ): InspectorState => {
19
- const host: ts.CompilerHost = {
20
- ...ts.createCompilerHost({}),
21
- getSourceFile: (name, languageVersion) =>
22
- name === SOURCE_FILE
23
- ? ts.createSourceFile(name, source, languageVersion, true)
24
- : undefined,
25
- fileExists: (name) => name === SOURCE_FILE,
26
- readFile: (name) => (name === SOURCE_FILE ? source : undefined),
27
- }
28
- const program = ts.createProgram([SOURCE_FILE], { noLib: true }, host)
29
- const checker = program.getTypeChecker()
30
- const sourceFile = program.getSourceFile(SOURCE_FILE)!
31
-
32
- const typesLookup = new Map<string, ts.Type[]>()
33
- const importMapFor = (name: string): PathToNameAndType => {
34
- const declaration = sourceFile.statements.find(
35
- (statement): statement is ts.InterfaceDeclaration =>
36
- ts.isInterfaceDeclaration(statement) && statement.name.text === name
37
- )!
38
- typesLookup.set(name, [checker.getTypeAtLocation(declaration)])
39
- return new Map([
40
- [SOURCE_FILE, [{ variable: name, type: name, typePath: SOURCE_FILE }]],
41
- ])
42
- }
43
-
44
- return {
45
- typesLookup,
46
- singletonServicesTypeImportMap: importMapFor(singletonName),
47
- wireServicesTypeImportMap: importMapFor(wireName),
48
- serviceAggregation: {
49
- requiredServices: new Set<string>(),
50
- usedFunctions: new Set<string>(),
51
- usedMiddleware: new Set<string>(),
52
- usedPermissions: new Set<string>(),
53
- allSingletonServices: [],
54
- allWireServices: [],
55
- },
56
- functions: { meta: {} },
57
- middleware: { definitions: {}, tagMiddleware: new Map() },
58
- permissions: { definitions: {} },
59
- http: {
60
- meta: {
61
- get: {},
62
- post: {},
63
- put: {},
64
- patch: {},
65
- delete: {},
66
- head: {},
67
- options: {},
68
- },
69
- routeMiddleware: new Map(),
70
- },
71
- channels: { meta: {} },
72
- queueWorkers: { meta: {} },
73
- scheduledTasks: { meta: {} },
74
- mcpEndpoints: { toolsMeta: {}, promptsMeta: {}, resourcesMeta: {} },
75
- agents: { agentsMeta: {} },
76
- workflows: { meta: {}, graphMeta: {} },
77
- wireServicesMeta: new Map(),
78
- rpc: { internalMeta: {}, exposedMeta: {} },
79
- scopes: { definitions: [] },
80
- addonFunctions: {},
81
- addonRequiredParentServices: [],
82
- } as unknown as InspectorState
83
- }
84
-
85
- const CONVENTIONAL = `
86
- interface SingletonServices { logger: string; kysely: string }
87
- interface Services { logger: string; kysely: string; http: string }
88
- `
89
-
90
- const RENAMED = `
91
- interface AppSingletonServices { logger: string; kysely: string }
92
- interface AppServices { logger: string; kysely: string; http: string }
93
- `
94
-
95
- describe('service extraction resolves core types by declaration, not by name', () => {
96
- test('the conventional names still resolve', () => {
97
- const state = stateWithCoreTypes(
98
- CONVENTIONAL,
99
- 'SingletonServices',
100
- 'Services'
101
- )
102
- aggregateRequiredServices(state)
103
- assert.deepEqual(state.serviceAggregation.allSingletonServices, [
104
- 'kysely',
105
- 'logger',
106
- ])
107
- assert.deepEqual(state.serviceAggregation.allWireServices, ['http'])
108
- })
109
-
110
- test('a project that renamed its interfaces resolves the same way', () => {
111
- const state = stateWithCoreTypes(
112
- RENAMED,
113
- 'AppSingletonServices',
114
- 'AppServices'
115
- )
116
- aggregateRequiredServices(state)
117
- assert.deepEqual(
118
- state.serviceAggregation.allSingletonServices,
119
- ['kysely', 'logger'],
120
- 'a renamed SingletonServices must not silently resolve to no services'
121
- )
122
- assert.deepEqual(state.serviceAggregation.allWireServices, ['http'])
123
- })
124
-
125
- test('no declaration at all leaves the lists empty for PKU724 to catch', () => {
126
- const state = stateWithCoreTypes(
127
- CONVENTIONAL,
128
- 'SingletonServices',
129
- 'Services'
130
- )
131
- state.singletonServicesTypeImportMap = new Map()
132
- state.wireServicesTypeImportMap = new Map()
133
- // Left at their initial empty value on purpose: `extractAllServices` only
134
- // assigns when a type resolves, so seeding them here would make the
135
- // assertions below true no matter what it did. `typesLookup` still holds
136
- // both interfaces under their conventional names — resolving one from
137
- // there is exactly the bug this guards.
138
- aggregateRequiredServices(state)
139
- assert.deepEqual(state.serviceAggregation.allSingletonServices, [])
140
- assert.deepEqual(state.serviceAggregation.allWireServices, [])
141
- })
142
- })
@@ -1,35 +0,0 @@
1
- import type ts from 'typescript'
2
- import type { PathToNameAndType } from '../types.js'
3
-
4
- /**
5
- * Resolves the one interface extending a core type (`CoreConfig`,
6
- * `CoreSingletonServices`, `CoreServices`) to the `ts.Type` the AST walk
7
- * recorded for it.
8
- *
9
- * `typesLookup` is keyed by whatever the project named the interface, so
10
- * reading it under a hardcoded `'SingletonServices'` only works for projects
11
- * that follow the scaffold's naming. A project that renamed the interface still
12
- * satisfies every required-type check and then resolves to no services at all,
13
- * which surfaces far downstream as PKU724 or as every service turning optional.
14
- * The import map carries the real name, so no name matching is needed.
15
- *
16
- * A project is only ever meant to declare one of each: `getMetaTypes` records
17
- * `More than one ... found` when it sees a second, which `checkRequiredTypes`
18
- * then reports. That check is the caller's to run, and extraction happens
19
- * first, so where two declarations do exist this takes the first in traversal
20
- * order rather than diagnosing the ambiguity itself.
21
- */
22
- export const resolveCoreType = (
23
- typesLookup: Map<string, ts.Type[]>,
24
- importMap: PathToNameAndType
25
- ): ts.Type | undefined => {
26
- for (const entries of importMap.values()) {
27
- for (const { type } of entries) {
28
- const resolved = type ? typesLookup.get(type)?.[0] : undefined
29
- if (resolved) {
30
- return resolved
31
- }
32
- }
33
- }
34
- return undefined
35
- }
@@ -1,135 +0,0 @@
1
- import { describe, test } from 'node:test'
2
- import assert from 'node:assert'
3
- import {
4
- IncompatibleDeployTargetError,
5
- resolveDeployTarget,
6
- } from './resolve-deploy-target.js'
7
-
8
- describe('resolveDeployTarget', () => {
9
- test('default → serverless', () => {
10
- assert.strictEqual(resolveDeployTarget({}, new Set()), 'serverless')
11
- })
12
-
13
- test('explicit deploy: server → server', () => {
14
- assert.strictEqual(
15
- resolveDeployTarget({ deploy: 'server' }, new Set()),
16
- 'server'
17
- )
18
- })
19
-
20
- test('explicit deploy: serverless with no incompatible svc → serverless', () => {
21
- assert.strictEqual(
22
- resolveDeployTarget(
23
- { deploy: 'serverless', services: { services: ['kysely'] } as any },
24
- new Set(['fs'])
25
- ),
26
- 'serverless'
27
- )
28
- })
29
-
30
- test('serverlessIncompatible service forces server even without deploy flag', () => {
31
- assert.strictEqual(
32
- resolveDeployTarget(
33
- { services: { services: ['metaService'] } as any },
34
- new Set(['metaService'])
35
- ),
36
- 'server'
37
- )
38
- })
39
-
40
- test('serverlessIncompatible takes precedence over explicit deploy: server', () => {
41
- assert.strictEqual(
42
- resolveDeployTarget(
43
- {
44
- deploy: 'server',
45
- services: { services: ['metaService'] } as any,
46
- },
47
- new Set(['metaService'])
48
- ),
49
- 'server'
50
- )
51
- })
52
-
53
- test('explicit deploy: serverless + incompatible svc → throws', () => {
54
- assert.throws(
55
- () =>
56
- resolveDeployTarget(
57
- {
58
- deploy: 'serverless',
59
- services: { services: ['metaService', 'unrelated'] } as any,
60
- },
61
- new Set(['metaService']),
62
- 'myFunction'
63
- ),
64
- (err: unknown) => {
65
- assert.ok(err instanceof IncompatibleDeployTargetError)
66
- assert.strictEqual(err.functionName, 'myFunction')
67
- assert.deepStrictEqual(err.incompatibleServices, ['metaService'])
68
- assert.match(err.message, /serverless-incompatible/)
69
- assert.match(err.message, /myFunction/)
70
- return true
71
- }
72
- )
73
- })
74
-
75
- test('multiple incompatible services are all reported', () => {
76
- assert.throws(
77
- () =>
78
- resolveDeployTarget(
79
- {
80
- deploy: 'serverless',
81
- services: {
82
- services: ['metaService', 'localContent'],
83
- } as any,
84
- },
85
- new Set(['metaService', 'localContent']),
86
- 'multiSvc'
87
- ),
88
- (err: unknown) => {
89
- assert.ok(err instanceof IncompatibleDeployTargetError)
90
- assert.deepStrictEqual(err.incompatibleServices, [
91
- 'metaService',
92
- 'localContent',
93
- ])
94
- return true
95
- }
96
- )
97
- })
98
-
99
- test('handles funcMeta with no services field', () => {
100
- assert.strictEqual(
101
- resolveDeployTarget({ deploy: 'server' }, new Set(['metaService'])),
102
- 'server'
103
- )
104
- })
105
-
106
- test('defaultTarget: server overrides the serverless default', () => {
107
- assert.strictEqual(
108
- resolveDeployTarget({}, new Set(), '<unknown>', 'server'),
109
- 'server'
110
- )
111
- })
112
-
113
- test('explicit deploy flag wins over defaultTarget', () => {
114
- assert.strictEqual(
115
- resolveDeployTarget({ deploy: 'serverless' }, new Set(), 'fn', 'server'),
116
- 'serverless'
117
- )
118
- assert.strictEqual(
119
- resolveDeployTarget({ deploy: 'server' }, new Set(), 'fn', 'serverless'),
120
- 'server'
121
- )
122
- })
123
-
124
- test('serverlessIncompatible still forces server regardless of defaultTarget', () => {
125
- assert.strictEqual(
126
- resolveDeployTarget(
127
- { services: { services: ['metaService'] } as any },
128
- new Set(['metaService']),
129
- 'fn',
130
- 'serverless'
131
- ),
132
- 'server'
133
- )
134
- })
135
- })
@@ -1,65 +0,0 @@
1
- import type { FunctionMeta } from '@pikku/core/services'
2
-
3
- /**
4
- * Thrown when a function's explicit `deploy: 'serverless'` conflicts
5
- * with one of its services being declared `serverlessIncompatible`.
6
- * The user has to either remove the explicit flag (let it auto-resolve
7
- * to 'server'), or set `deploy: 'server'` explicitly.
8
- */
9
- export class IncompatibleDeployTargetError extends Error {
10
- constructor(
11
- public readonly functionName: string,
12
- public readonly incompatibleServices: string[]
13
- ) {
14
- super(
15
- `Function '${functionName}' is declared deploy: 'serverless' but uses ` +
16
- `serverless-incompatible service(s) [${incompatibleServices.join(', ')}]. ` +
17
- `Either remove deploy: 'serverless' (will auto-resolve to 'server'), ` +
18
- `or set deploy: 'server' explicitly.`
19
- )
20
- this.name = 'IncompatibleDeployTargetError'
21
- }
22
- }
23
-
24
- /**
25
- * Determine the effective deploy target for a function.
26
- *
27
- * Resolution order:
28
- * 1. If any of the function's services is in `serverlessIncompatible`:
29
- * - throw if the function explicitly declares `deploy: 'serverless'`
30
- * - otherwise target is 'server'
31
- * 2. Explicit `funcMeta.deploy: 'serverless' | 'server'`
32
- * 3. `defaultTarget` (sourced from `pikku.config.json` →
33
- * `deploy.defaultTarget`, falling back to 'serverless')
34
- *
35
- * Used both by the per-unit deploy analyzer (when bucketing functions
36
- * into deployment units) and by `filterInspectorState` (when
37
- * `pikku all --deploy <target>` is used to emit a target-scoped set
38
- * of gen files).
39
- */
40
- export function resolveDeployTarget(
41
- funcMeta: Pick<FunctionMeta, 'deploy' | 'services'>,
42
- serverlessIncompatible: Set<string>,
43
- functionName = '<unknown>',
44
- defaultTarget: 'serverless' | 'server' = 'serverless'
45
- ): 'serverless' | 'server' {
46
- // Service compatibility wins over the explicit flag — a serverless
47
- // bundle of a function that needs (e.g.) node:fs would crash at runtime.
48
- const incompatibleHits: string[] = []
49
- if (funcMeta.services?.services) {
50
- for (const svc of funcMeta.services.services) {
51
- if (serverlessIncompatible.has(svc)) incompatibleHits.push(svc)
52
- }
53
- }
54
-
55
- if (incompatibleHits.length > 0) {
56
- if (funcMeta.deploy === 'serverless') {
57
- throw new IncompatibleDeployTargetError(functionName, incompatibleHits)
58
- }
59
- return 'server'
60
- }
61
-
62
- if (funcMeta.deploy === 'server') return 'server'
63
- if (funcMeta.deploy === 'serverless') return 'serverless'
64
- return defaultTarget
65
- }
@@ -1,25 +0,0 @@
1
- import type { FunctionMeta, FunctionsMeta } from '@pikku/core/services'
2
-
3
- /**
4
- * Look up function metadata by pikkuFuncId, checking both local functions
5
- * and addon functions. Addon functions use namespaced IDs like 'namespace:funcName'.
6
- */
7
- export function resolveFunctionMeta(
8
- state: {
9
- functions: { meta: FunctionsMeta }
10
- addonFunctions: Record<string, FunctionsMeta>
11
- },
12
- pikkuFuncId: string
13
- ): FunctionMeta | undefined {
14
- // Check local functions first
15
- const local = state.functions.meta[pikkuFuncId]
16
- if (local) return local
17
-
18
- // Check addon functions (namespaced like 'swaggerPetstore:addPet')
19
- const colonIndex = pikkuFuncId.indexOf(':')
20
- if (colonIndex === -1) return undefined
21
-
22
- const namespace = pikkuFuncId.substring(0, colonIndex)
23
- const funcName = pikkuFuncId.substring(colonIndex + 1)
24
- return state.addonFunctions[namespace]?.[funcName]
25
- }
@@ -1,42 +0,0 @@
1
- import type { TypesMap } from '../types-map.js'
2
- import type { FunctionsMeta } from '@pikku/core/services'
3
-
4
- export function resolveFunctionIOTypes(
5
- pikkuFuncId: string,
6
- functionsMeta: FunctionsMeta,
7
- typesMap: TypesMap,
8
- requiredTypes: Set<string>
9
- ): { inputType: string; outputType: string } {
10
- const functionMeta = functionsMeta[pikkuFuncId]
11
- if (!functionMeta) {
12
- throw new Error(
13
- `Function ${pikkuFuncId} not found in functionsMeta. Please check your configuration.`
14
- )
15
- }
16
-
17
- const input = functionMeta.inputs ? functionMeta.inputs[0] : undefined
18
- const output = functionMeta.outputs ? functionMeta.outputs[0] : undefined
19
-
20
- let inputType = 'null'
21
- if (input) {
22
- try {
23
- inputType = typesMap.getTypeMeta(input).uniqueName
24
- } catch {
25
- inputType = input
26
- }
27
- }
28
-
29
- let outputType = 'null'
30
- if (output) {
31
- try {
32
- outputType = typesMap.getTypeMeta(output).uniqueName
33
- } catch {
34
- outputType = output
35
- }
36
- }
37
-
38
- requiredTypes.add(inputType)
39
- requiredTypes.add(outputType)
40
-
41
- return { inputType, outputType }
42
- }
@@ -1,46 +0,0 @@
1
- import * as ts from 'typescript'
2
-
3
- /**
4
- * Resolve an identifier to its definition, optionally unwrapping
5
- * known "define" wrapper functions (e.g. defineHTTPRoutes, defineCLICommands).
6
- *
7
- * When the identifier resolves to a variable whose initializer is a call
8
- * to one of the `unwrapFunctionNames`, the first argument of that call
9
- * is returned instead.
10
- */
11
- export function resolveIdentifier(
12
- node: ts.Identifier,
13
- checker: ts.TypeChecker,
14
- unwrapFunctionNames?: string[]
15
- ): ts.Node | undefined {
16
- const symbol = checker.getSymbolAtLocation(node)
17
- if (!symbol) return undefined
18
-
19
- // Handle aliased symbols (imports)
20
- let resolvedSymbol = symbol
21
- if (resolvedSymbol.flags & ts.SymbolFlags.Alias) {
22
- resolvedSymbol = checker.getAliasedSymbol(resolvedSymbol) ?? resolvedSymbol
23
- }
24
-
25
- const decl =
26
- resolvedSymbol.valueDeclaration || resolvedSymbol.declarations?.[0]
27
- if (!decl) return undefined
28
-
29
- // Follow to the actual value (handles imports, variable declarations)
30
- if (ts.isVariableDeclaration(decl) && decl.initializer) {
31
- // Check if it's a call to one of the unwrap functions
32
- if (
33
- ts.isCallExpression(decl.initializer) &&
34
- unwrapFunctionNames &&
35
- unwrapFunctionNames.length > 0
36
- ) {
37
- const expr = decl.initializer.expression
38
- if (ts.isIdentifier(expr) && unwrapFunctionNames.includes(expr.text)) {
39
- return decl.initializer.arguments[0]
40
- }
41
- }
42
- return decl.initializer
43
- }
44
-
45
- return undefined
46
- }
@@ -1,71 +0,0 @@
1
- import * as ts from 'typescript'
2
-
3
- export interface RefContractResolution<T> {
4
- contract: T
5
- /**
6
- * Optional basePath override supplied by the consumer via the second
7
- * argument, e.g. refHTTP('ns:routes', { basePath: '/ext' }). When undefined
8
- * the addon contract's own basePath is preserved.
9
- */
10
- basePath?: string
11
- }
12
-
13
- const getStringProperty = (
14
- obj: ts.ObjectLiteralExpression,
15
- name: string
16
- ): string | undefined => {
17
- for (const prop of obj.properties) {
18
- if (
19
- ts.isPropertyAssignment(prop) &&
20
- (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name)) &&
21
- prop.name.text === name &&
22
- ts.isStringLiteral(prop.initializer)
23
- ) {
24
- return prop.initializer.text
25
- }
26
- }
27
- return undefined
28
- }
29
-
30
- /**
31
- * Resolve a refHTTP / refChannel / refCLI call expression against the addon
32
- * contracts already loaded (and namespaced) by loadAddonFunctionsMeta.
33
- *
34
- * The first string argument has the form 'namespace:contractName', mirroring
35
- * how ref('namespace:fn') references an addon function. Detection is purely
36
- * syntactic — no import resolution is required because the namespace and
37
- * contract name are carried in the string literal. An optional second object
38
- * argument may override mount details such as basePath.
39
- */
40
- export const resolveRefContract = <T>(
41
- node: ts.Node,
42
- helperName: 'refHTTP' | 'refChannel' | 'refCLI',
43
- addonContracts: Record<string, Record<string, T>>
44
- ): RefContractResolution<T> | null => {
45
- if (!ts.isCallExpression(node)) return null
46
- if (
47
- !ts.isIdentifier(node.expression) ||
48
- node.expression.text !== helperName
49
- ) {
50
- return null
51
- }
52
-
53
- const [arg, optionsArg] = node.arguments
54
- if (!arg || !ts.isStringLiteral(arg)) return null
55
-
56
- const separator = arg.text.indexOf(':')
57
- if (separator === -1) return null
58
-
59
- const namespace = arg.text.slice(0, separator)
60
- const contractName = arg.text.slice(separator + 1)
61
-
62
- const contract = addonContracts[namespace]?.[contractName]
63
- if (contract === undefined) return null
64
-
65
- let basePath: string | undefined
66
- if (optionsArg && ts.isObjectLiteralExpression(optionsArg)) {
67
- basePath = getStringProperty(optionsArg, 'basePath')
68
- }
69
-
70
- return { contract, basePath }
71
- }