@pikku/inspector 0.12.75 → 0.12.82

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 (223) hide show
  1. package/CHANGELOG.md +357 -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-middleware.js +66 -42
  8. package/dist/add/add-wire-addon.js +7 -4
  9. package/dist/add/add-wire-remote-addon.js +1 -0
  10. package/dist/error-codes.d.ts +1 -0
  11. package/dist/error-codes.js +1 -0
  12. package/dist/index.d.ts +3 -1
  13. package/dist/index.js +3 -1
  14. package/dist/inspector.js +5 -0
  15. package/dist/surface/collect-surface.d.ts +58 -0
  16. package/dist/surface/collect-surface.js +613 -0
  17. package/dist/types.d.ts +36 -12
  18. package/dist/utils/filter-inspector-state.js +14 -0
  19. package/dist/utils/load-addon-functions-meta.js +24 -11
  20. package/dist/utils/post-process.js +15 -0
  21. package/dist/utils/read-module-specifiers.d.ts +8 -0
  22. package/dist/utils/read-module-specifiers.js +23 -0
  23. package/dist/utils/read-tsconfig-out-dir.d.ts +5 -0
  24. package/dist/utils/read-tsconfig-out-dir.js +12 -0
  25. package/dist/utils/resolve-deploy-target.d.ts +7 -0
  26. package/dist/utils/resolve-deploy-target.js +15 -7
  27. package/dist/utils/serialize-inspector-state.d.ts +6 -0
  28. package/dist/utils/serialize-inspector-state.js +10 -0
  29. package/dist/visit.js +4 -0
  30. package/package.json +16 -6
  31. package/run-tests.sh +21 -6
  32. package/src/add/add-addon-bans.ts +0 -85
  33. package/src/add/add-agent-scorer.test.ts +0 -197
  34. package/src/add/add-agent-scorer.ts +0 -83
  35. package/src/add/add-agent.test.ts +0 -87
  36. package/src/add/add-agent.ts +0 -638
  37. package/src/add/add-approval-description.ts +0 -76
  38. package/src/add/add-auth.test.ts +0 -791
  39. package/src/add/add-auth.ts +0 -424
  40. package/src/add/add-channel.ts +0 -778
  41. package/src/add/add-cli-renderers.test.ts +0 -74
  42. package/src/add/add-cli.ts +0 -1006
  43. package/src/add/add-credential.ts +0 -186
  44. package/src/add/add-feature.test.ts +0 -306
  45. package/src/add/add-feature.ts +0 -253
  46. package/src/add/add-file-extends-core-type.ts +0 -66
  47. package/src/add/add-file-with-config.ts +0 -45
  48. package/src/add/add-file-with-factory.test.ts +0 -134
  49. package/src/add/add-file-with-factory.ts +0 -288
  50. package/src/add/add-functions.test.ts +0 -743
  51. package/src/add/add-functions.ts +0 -1548
  52. package/src/add/add-gateway.ts +0 -143
  53. package/src/add/add-http-route-ref.test.ts +0 -149
  54. package/src/add/add-http-route.ts +0 -530
  55. package/src/add/add-http-routes.ts +0 -471
  56. package/src/add/add-keyed-wiring.ts +0 -180
  57. package/src/add/add-mcp-prompt.ts +0 -157
  58. package/src/add/add-mcp-resource.ts +0 -175
  59. package/src/add/add-middleware.test.ts +0 -196
  60. package/src/add/add-middleware.ts +0 -879
  61. package/src/add/add-permission-auth.test.ts +0 -59
  62. package/src/add/add-permission.ts +0 -311
  63. package/src/add/add-personas.test.ts +0 -406
  64. package/src/add/add-personas.ts +0 -287
  65. package/src/add/add-queue-worker.ts +0 -117
  66. package/src/add/add-rpc-invocations.test.ts +0 -287
  67. package/src/add/add-rpc-invocations.ts +0 -221
  68. package/src/add/add-scenario-step.test.ts +0 -509
  69. package/src/add/add-schedule.ts +0 -120
  70. package/src/add/add-scope.test.ts +0 -419
  71. package/src/add/add-scope.ts +0 -221
  72. package/src/add/add-secret.ts +0 -10
  73. package/src/add/add-subject-scenario-steps.test.ts +0 -161
  74. package/src/add/add-system-role.test.ts +0 -297
  75. package/src/add/add-system-role.ts +0 -162
  76. package/src/add/add-trigger.ts +0 -165
  77. package/src/add/add-variable.ts +0 -10
  78. package/src/add/add-wire-addon.test.ts +0 -185
  79. package/src/add/add-wire-addon.ts +0 -146
  80. package/src/add/add-wire-remote-addon.ts +0 -80
  81. package/src/add/add-workflow-edge-cases.test.ts +0 -201
  82. package/src/add/add-workflow-fanout.test.ts +0 -166
  83. package/src/add/add-workflow-graph-foreach.test.ts +0 -174
  84. package/src/add/add-workflow-graph-input.test.ts +0 -94
  85. package/src/add/add-workflow-graph-notes.test.ts +0 -74
  86. package/src/add/add-workflow-graph.ts +0 -644
  87. package/src/add/add-workflow-on-error.test.ts +0 -118
  88. package/src/add/add-workflow-return-switch.test.ts +0 -121
  89. package/src/add/add-workflow-silent-drops.test.ts +0 -261
  90. package/src/add/add-workflow-sleep-expression.test.ts +0 -96
  91. package/src/add/add-workflow.test.ts +0 -155
  92. package/src/add/add-workflow.ts +0 -482
  93. package/src/add/addon-bans.test.ts +0 -121
  94. package/src/add/addon-contracts.test.ts +0 -221
  95. package/src/add/allow-escape-hatches.test.ts +0 -144
  96. package/src/add/audit-meta.test.ts +0 -75
  97. package/src/add/classified-sink-check.test.ts +0 -347
  98. package/src/add/config-value-metadata.test.ts +0 -263
  99. package/src/add/dynamic-import-check.test.ts +0 -119
  100. package/src/add/expect-eventually-scenario-only.test.ts +0 -108
  101. package/src/add/function-body-span.test.ts +0 -96
  102. package/src/add/inline-wiring-function.test.ts +0 -104
  103. package/src/add/pii-check.test.ts +0 -258
  104. package/src/add/rpc-type-cast.test.ts +0 -123
  105. package/src/add/scenario-instrumentation.test.ts +0 -89
  106. package/src/add/scenario-instrumentation.ts +0 -42
  107. package/src/add/scenario-marker.test.ts +0 -89
  108. package/src/add/scenario-not-extractable.test.ts +0 -159
  109. package/src/add/scenario-services.test.ts +0 -103
  110. package/src/add/scenario-skip.test.ts +0 -83
  111. package/src/add/validate-workflow-graph-addons.test.ts +0 -102
  112. package/src/add/wire-name-literal.test.ts +0 -117
  113. package/src/error-codes-emitted.test.ts +0 -76
  114. package/src/error-codes.ts +0 -145
  115. package/src/index.ts +0 -66
  116. package/src/inspector.ts +0 -575
  117. package/src/schema-generator.ts +0 -1
  118. package/src/source-file-cache.ts +0 -73
  119. package/src/tsconfig-paths.test.ts +0 -90
  120. package/src/types-map.ts +0 -141
  121. package/src/types.ts +0 -775
  122. package/src/utils/annotate-http-route-auth.test.ts +0 -161
  123. package/src/utils/annotate-http-route-auth.ts +0 -53
  124. package/src/utils/check-classified-sinks.ts +0 -215
  125. package/src/utils/check-pii-output.ts +0 -120
  126. package/src/utils/compute-diagnostics.test.ts +0 -106
  127. package/src/utils/compute-required-schemas.ts +0 -48
  128. package/src/utils/contract-hashes.test.ts +0 -553
  129. package/src/utils/contract-hashes.ts +0 -387
  130. package/src/utils/custom-types-generator.test.ts +0 -124
  131. package/src/utils/custom-types-generator.ts +0 -150
  132. package/src/utils/detect-schema-vendor.test.ts +0 -123
  133. package/src/utils/detect-schema-vendor.ts +0 -120
  134. package/src/utils/does-type-extend-core-type.ts +0 -53
  135. package/src/utils/ensure-function-metadata.ts +0 -385
  136. package/src/utils/extract-function-name.ts +0 -612
  137. package/src/utils/extract-function-node.ts +0 -70
  138. package/src/utils/extract-node-value.test.ts +0 -117
  139. package/src/utils/extract-node-value.ts +0 -172
  140. package/src/utils/extract-secret-usage.test.ts +0 -81
  141. package/src/utils/extract-secret-usage.ts +0 -73
  142. package/src/utils/extract-services.ts +0 -70
  143. package/src/utils/extract-surface-usage.test.ts +0 -147
  144. package/src/utils/extract-surface-usage.ts +0 -121
  145. package/src/utils/filter-inspector-state.test.ts +0 -2232
  146. package/src/utils/filter-inspector-state.ts +0 -1298
  147. package/src/utils/filter-utils.test.ts +0 -843
  148. package/src/utils/filter-utils.ts +0 -161
  149. package/src/utils/find-root-dir.ts +0 -68
  150. package/src/utils/find-zod-transform.test.ts +0 -70
  151. package/src/utils/find-zod-transform.ts +0 -103
  152. package/src/utils/get-exported-variable-name.ts +0 -48
  153. package/src/utils/get-files-and-methods.ts +0 -157
  154. package/src/utils/get-property-value.test.ts +0 -141
  155. package/src/utils/get-property-value.ts +0 -331
  156. package/src/utils/hash.ts +0 -26
  157. package/src/utils/literal-properties.ts +0 -97
  158. package/src/utils/load-addon-functions-meta.test.ts +0 -157
  159. package/src/utils/load-addon-functions-meta.ts +0 -508
  160. package/src/utils/load-addon-scopes.test.ts +0 -138
  161. package/src/utils/middleware.test.ts +0 -204
  162. package/src/utils/middleware.ts +0 -303
  163. package/src/utils/nested-project-filter.test.ts +0 -53
  164. package/src/utils/nested-project-filter.ts +0 -28
  165. package/src/utils/permissions-shorthand.test.ts +0 -79
  166. package/src/utils/permissions.test.ts +0 -125
  167. package/src/utils/permissions.ts +0 -129
  168. package/src/utils/post-process.test.ts +0 -1094
  169. package/src/utils/post-process.ts +0 -1604
  170. package/src/utils/resolve-addon-package.ts +0 -106
  171. package/src/utils/resolve-core-type.test.ts +0 -142
  172. package/src/utils/resolve-core-type.ts +0 -35
  173. package/src/utils/resolve-deploy-target.test.ts +0 -135
  174. package/src/utils/resolve-deploy-target.ts +0 -65
  175. package/src/utils/resolve-function-meta.ts +0 -25
  176. package/src/utils/resolve-function-types.ts +0 -42
  177. package/src/utils/resolve-identifier.ts +0 -46
  178. package/src/utils/resolve-ref-contract.ts +0 -71
  179. package/src/utils/resolve-versions.test.ts +0 -499
  180. package/src/utils/resolve-versions.ts +0 -228
  181. package/src/utils/schema-cache.test.ts +0 -124
  182. package/src/utils/schema-generator.test.ts +0 -272
  183. package/src/utils/schema-generator.ts +0 -780
  184. package/src/utils/secret-alias-services.test.ts +0 -146
  185. package/src/utils/secret-alias-services.ts +0 -58
  186. package/src/utils/serialize-inspector-state.ts +0 -742
  187. package/src/utils/serialize-mcp-json.ts +0 -150
  188. package/src/utils/serialize-middleware-groups-meta.ts +0 -36
  189. package/src/utils/serialize-openapi-json.ts +0 -277
  190. package/src/utils/serialize-permissions-groups-meta.ts +0 -7
  191. package/src/utils/single-declaration.ts +0 -57
  192. package/src/utils/type-utils.ts +0 -145
  193. package/src/utils/validate-auth-sessionless.ts +0 -30
  194. package/src/utils/validate-exposed-functions-gated.test.ts +0 -230
  195. package/src/utils/validate-exposed-functions-gated.ts +0 -103
  196. package/src/utils/validate-tags-resolve-to-middleware.test.ts +0 -108
  197. package/src/utils/validate-tags-resolve-to-middleware.ts +0 -48
  198. package/src/utils/workflow/derive-workflow-plan.test.ts +0 -178
  199. package/src/utils/workflow/derive-workflow-plan.ts +0 -117
  200. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +0 -1465
  201. package/src/utils/workflow/dsl/deserialize-fanout-body.test.ts +0 -71
  202. package/src/utils/workflow/dsl/deserialize-fanout-fidelity.test.ts +0 -99
  203. package/src/utils/workflow/dsl/deserialize-graph-next.test.ts +0 -161
  204. package/src/utils/workflow/dsl/deserialize-roundtrip-bindings.test.ts +0 -121
  205. package/src/utils/workflow/dsl/deserialize-roundtrip-values.test.ts +0 -116
  206. package/src/utils/workflow/dsl/deserialize-roundtrip.test.ts +0 -145
  207. package/src/utils/workflow/dsl/extract-approval-policy.test.ts +0 -71
  208. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +0 -2254
  209. package/src/utils/workflow/dsl/index.ts +0 -11
  210. package/src/utils/workflow/dsl/patterns.ts +0 -440
  211. package/src/utils/workflow/dsl/validation.ts +0 -203
  212. package/src/utils/workflow/graph/convert-dsl-to-graph-skip.test.ts +0 -35
  213. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +0 -543
  214. package/src/utils/workflow/graph/fanout-node-identity.test.ts +0 -105
  215. package/src/utils/workflow/graph/finalize-workflow-wires.ts +0 -310
  216. package/src/utils/workflow/graph/finalize-workflows.test.ts +0 -50
  217. package/src/utils/workflow/graph/finalize-workflows.ts +0 -130
  218. package/src/utils/workflow/graph/index.ts +0 -28
  219. package/src/utils/workflow/graph/serialize-workflow-graph.ts +0 -240
  220. package/src/utils/workflow/graph/workflow-graph.types.ts +0 -281
  221. package/src/visit.ts +0 -181
  222. package/tsconfig.json +0 -19
  223. 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
- }