@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,161 +0,0 @@
1
- import type { InspectorFilters, InspectorLogger } from '../types.js'
2
- import type { PikkuWiringTypes } from '@pikku/core/types'
3
-
4
- /**
5
- * Match a value against a pattern with wildcard support
6
- * Supports "*" at the beginning, end, or both (e.g., "send*", "*Payment", "*process*")
7
- * @param value - The value to check
8
- * @param pattern - The pattern with optional "*" wildcard(s)
9
- */
10
- export function matchesWildcard(value: string, pattern: string): boolean {
11
- // If pattern is just '*', match everything
12
- if (pattern === '*') {
13
- return true
14
- }
15
-
16
- const startsWithWildcard = pattern.startsWith('*')
17
- const endsWithWildcard = pattern.endsWith('*')
18
-
19
- if (startsWithWildcard && endsWithWildcard) {
20
- // Pattern like "*middle*" - check if value contains the middle part
21
- const middle = pattern.slice(1, -1)
22
- if (middle === '') {
23
- return true // Pattern is "**", match everything
24
- }
25
- return value.includes(middle)
26
- } else if (startsWithWildcard) {
27
- // Pattern like "*suffix" - check if value ends with suffix and has content before
28
- const suffix = pattern.slice(1)
29
- return value.endsWith(suffix) && value.length > suffix.length
30
- } else if (endsWithWildcard) {
31
- // Pattern like "prefix*" - check if value starts with prefix and has content after
32
- const prefix = pattern.slice(0, -1)
33
- return value.startsWith(prefix) && value.length > prefix.length
34
- }
35
-
36
- // No wildcard, exact match
37
- return value === pattern
38
- }
39
-
40
- export const matchesFilters = (
41
- filters: InspectorFilters,
42
- params: {
43
- tags?: string[]
44
- name?: string // Wire/function name for name filter
45
- },
46
- meta: {
47
- type: PikkuWiringTypes
48
- name: string
49
- filePath?: string
50
- httpRoute?: string // For HTTP route filtering
51
- httpMethod?: string // For HTTP method filtering
52
- },
53
- logger: InspectorLogger
54
- ) => {
55
- // If no filters are provided, allow everything
56
- if (Object.keys(filters).length === 0) {
57
- return true
58
- }
59
-
60
- // If all filter arrays are empty, allow everything
61
- if (
62
- (!filters.names || filters.names.length === 0) &&
63
- (!filters.tags || filters.tags.length === 0) &&
64
- (!filters.wires || filters.wires.length === 0) &&
65
- (!filters.excludeWires || filters.excludeWires.length === 0) &&
66
- (!filters.directories || filters.directories.length === 0) &&
67
- (!filters.httpRoutes || filters.httpRoutes.length === 0) &&
68
- (!filters.httpMethods || filters.httpMethods.length === 0)
69
- ) {
70
- return true
71
- }
72
-
73
- // Check wire include filter
74
- if (filters.wires && filters.wires.length > 0) {
75
- if (!filters.wires.includes(meta.type)) {
76
- logger.debug(`⒡ Filtered by wire include: ${meta.type}:${meta.name}`)
77
- return false
78
- }
79
- }
80
-
81
- // Check wire exclude filter
82
- if (filters.excludeWires && filters.excludeWires.length > 0) {
83
- if (filters.excludeWires.includes(meta.type)) {
84
- logger.debug(`⒡ Filtered by wire exclude: ${meta.type}:${meta.name}`)
85
- return false
86
- }
87
- }
88
-
89
- // Check directory filter
90
- if (filters.directories && filters.directories.length > 0) {
91
- if (!meta.filePath) {
92
- logger.debug(
93
- `⒡ Filtered by directory: ${meta.type}:${meta.name} (${meta.filePath})`
94
- )
95
- return false
96
- }
97
-
98
- const matchesDirectory = filters.directories.some((dir) => {
99
- // Normalize paths for comparison
100
- const normalizedFilePath = meta.filePath!.replace(/\\/g, '/')
101
- const normalizedDir = dir.replace(/\\/g, '/')
102
- return normalizedFilePath.includes(normalizedDir)
103
- })
104
-
105
- if (!matchesDirectory) {
106
- logger.debug(
107
- `⒡ Filtered by directory: ${meta.type}:${meta.name} (${meta.filePath})`
108
- )
109
- return false
110
- }
111
- }
112
-
113
- // Check tag filter
114
- if (filters.tags && filters.tags.length > 0) {
115
- if (
116
- !params.tags ||
117
- !filters.tags.some((tag) => params.tags!.includes(tag))
118
- ) {
119
- logger.debug(`⒡ Filtered by tags: ${meta.type}:${meta.name}`)
120
- return false
121
- }
122
- }
123
-
124
- // Check name filter (with wildcard support)
125
- if (filters.names && filters.names.length > 0) {
126
- const nameToMatch = params.name || meta.name
127
- const nameMatches = filters.names.some((pattern) =>
128
- matchesWildcard(nameToMatch, pattern)
129
- )
130
- if (!nameMatches) {
131
- logger.debug(`⒡ Filtered by name: ${meta.type}:${meta.name}`)
132
- return false
133
- }
134
- }
135
-
136
- // Check HTTP route filter (with wildcard support)
137
- if (filters.httpRoutes && filters.httpRoutes.length > 0 && meta.httpRoute) {
138
- const routeMatches = filters.httpRoutes.some((pattern) =>
139
- matchesWildcard(meta.httpRoute!, pattern)
140
- )
141
- if (!routeMatches) {
142
- logger.debug(`⒡ Filtered by HTTP route: ${meta.httpRoute}`)
143
- return false
144
- }
145
- }
146
-
147
- // Check HTTP method filter
148
- if (
149
- filters.httpMethods &&
150
- filters.httpMethods.length > 0 &&
151
- meta.httpMethod
152
- ) {
153
- const normalizedMethod = meta.httpMethod.toUpperCase()
154
- if (!filters.httpMethods.includes(normalizedMethod)) {
155
- logger.debug(`⒡ Filtered by HTTP method: ${meta.httpMethod}`)
156
- return false
157
- }
158
- }
159
-
160
- return true
161
- }
@@ -1,68 +0,0 @@
1
- import path from 'path'
2
-
3
- /**
4
- * Finds the common ancestor directory of all the given file paths.
5
- * This is used to determine the project root directory.
6
- *
7
- * @param filePaths - Array of absolute file paths
8
- * @returns The common ancestor directory path
9
- *
10
- * @example
11
- * findCommonAncestor([
12
- * '/Users/yasser/git/pikku/pikku/src/functions/a.ts',
13
- * '/Users/yasser/git/pikku/pikku/src/routes/b.ts'
14
- * ])
15
- * // Returns: '/Users/yasser/git/pikku/pikku'
16
- */
17
- export function findCommonAncestor(filePaths: string[]): string {
18
- if (filePaths.length === 0) {
19
- return process.cwd()
20
- }
21
-
22
- if (filePaths.length === 1) {
23
- return path.dirname(filePaths[0]!)
24
- }
25
-
26
- // Normalize all paths and get their directory parts
27
- const normalizedPaths = filePaths.map((p) =>
28
- path.dirname(path.normalize(p)).split(path.sep)
29
- )
30
-
31
- // Start with the first path's parts
32
- const firstPath = normalizedPaths[0]!
33
- let commonParts: string[] = []
34
-
35
- // Check each part of the first path
36
- for (let i = 0; i < firstPath.length; i++) {
37
- const part = firstPath[i]!
38
-
39
- // Check if this part exists in all other paths at the same position
40
- const existsInAll = normalizedPaths.every(
41
- (pathParts) => pathParts[i] === part
42
- )
43
-
44
- if (existsInAll) {
45
- commonParts.push(part)
46
- } else {
47
- break
48
- }
49
- }
50
-
51
- // If no common parts, return root
52
- if (commonParts.length === 0) {
53
- return path.sep
54
- }
55
-
56
- return commonParts.join(path.sep)
57
- }
58
-
59
- /**
60
- * Converts an absolute file path to a relative path from the root directory.
61
- *
62
- * @param absolutePath - The absolute file path
63
- * @param rootDir - The root directory to make the path relative to
64
- * @returns A relative path from rootDir to the file
65
- */
66
- export function toRelativePath(absolutePath: string, rootDir: string): string {
67
- return path.relative(rootDir, absolutePath)
68
- }
@@ -1,70 +0,0 @@
1
- import { test } from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import * as z from 'zod'
4
- import { findZodTransform } from './find-zod-transform.js'
5
-
6
- test('a schema with no transform is reported as clean', () => {
7
- assert.equal(findZodTransform(z.object({ id: z.string() })), undefined)
8
- assert.equal(findZodTransform(z.string().min(1).trim()), undefined)
9
- // a refinement narrows the value, it does not change its shape
10
- assert.equal(
11
- findZodTransform(z.object({ id: z.string() }).refine(() => true)),
12
- undefined
13
- )
14
- })
15
-
16
- test('a transform on the schema itself is found at the root', () => {
17
- assert.equal(
18
- findZodTransform(z.object({ id: z.string() }).transform((v) => v)),
19
- ''
20
- )
21
- })
22
-
23
- test('a transform on a field is found by path', () => {
24
- assert.equal(
25
- findZodTransform(
26
- z.object({
27
- id: z.string(),
28
- when: z.string().transform((s) => new Date(s)),
29
- })
30
- ),
31
- '.when'
32
- )
33
- })
34
-
35
- test('a transform is found through wrappers, arrays and unions', () => {
36
- assert.equal(
37
- findZodTransform(
38
- z.object({ tags: z.array(z.string().transform((s) => s.trim())) })
39
- ),
40
- '.tags[]'
41
- )
42
- assert.equal(
43
- findZodTransform(z.object({ n: z.string().transform(Number).optional() })),
44
- '.n'
45
- )
46
- assert.equal(
47
- findZodTransform(
48
- z.object({ v: z.union([z.string(), z.number().transform(String)]) })
49
- ),
50
- '.v|1'
51
- )
52
- assert.equal(
53
- findZodTransform(
54
- z.object({
55
- m: z.record(
56
- z.string(),
57
- z.string().transform((s) => s)
58
- ),
59
- })
60
- ),
61
- '.m[]'
62
- )
63
- })
64
-
65
- test('a self-referencing schema terminates instead of recursing forever', () => {
66
- const node: z.ZodType = z.lazy(() =>
67
- z.object({ name: z.string(), children: z.array(node) })
68
- )
69
- assert.equal(findZodTransform(node), undefined)
70
- })
@@ -1,103 +0,0 @@
1
- /**
2
- * Locate a `.transform()` anywhere inside a Zod schema.
3
- *
4
- * A transform is why a schema cannot be a function's `input`: the value the
5
- * caller sends and the value the function receives are different, and a wire
6
- * contract has to describe one shape. Zod says so twice, in two different
7
- * places, and neither says it usefully on its own — `toJSONSchema` with
8
- * `unrepresentable: 'any'` quietly yields `{}`, a schema that accepts
9
- * everything, and `zodToTs` throws about TypeScript rather than about the
10
- * wiring. Finding it up front lets the diagnostic name the schema, the path,
11
- * and the call.
12
- *
13
- * Zod 4 models `x.transform(fn)` as a `pipe` whose `out` is a `transform`, so
14
- * the walk looks for that node rather than for the method.
15
- */
16
-
17
- /** Max nodes visited, so a pathological or cyclic schema cannot hang the walk. */
18
- const MAX_NODES = 10_000
19
-
20
- interface ZodNode {
21
- _zod?: { def?: Record<string, any> }
22
- }
23
-
24
- const isNode = (value: unknown): value is ZodNode =>
25
- typeof value === 'object' &&
26
- value !== null &&
27
- typeof (value as ZodNode)._zod?.def?.type === 'string'
28
-
29
- /**
30
- * The path to the first `.transform()` found — `''` when the transform is the
31
- * schema itself, `'.user.email'` when it is nested — or `undefined` when there
32
- * is none.
33
- */
34
- export function findZodTransform(schema: unknown): string | undefined {
35
- const seen = new Set<unknown>()
36
- let budget = MAX_NODES
37
-
38
- const walk = (node: unknown, path: string): string | undefined => {
39
- if (budget-- <= 0 || !isNode(node) || seen.has(node)) return undefined
40
- seen.add(node)
41
-
42
- const def = node._zod!.def!
43
- if (def.type === 'transform') return path
44
-
45
- const children: Array<[unknown, string]> = []
46
- switch (def.type) {
47
- case 'object':
48
- case 'interface':
49
- for (const [key, child] of Object.entries(def.shape ?? {})) {
50
- children.push([child, `${path}.${key}`])
51
- }
52
- break
53
- case 'array':
54
- case 'set':
55
- children.push([def.element, `${path}[]`])
56
- break
57
- case 'tuple':
58
- for (const [i, child] of (def.items ?? []).entries()) {
59
- children.push([child, `${path}[${i}]`])
60
- }
61
- if (def.rest) children.push([def.rest, `${path}[]`])
62
- break
63
- case 'union':
64
- for (const [i, child] of (def.options ?? []).entries()) {
65
- children.push([child, `${path}|${i}`])
66
- }
67
- break
68
- case 'intersection':
69
- children.push([def.left, path], [def.right, path])
70
- break
71
- case 'record':
72
- case 'map':
73
- children.push(
74
- [def.keyType, `${path}[key]`],
75
- [def.valueType, `${path}[]`]
76
- )
77
- break
78
- case 'pipe':
79
- // `.transform()` itself: report the pipe's own path, not `out`'s.
80
- children.push([def.in, path], [def.out, path])
81
- break
82
- default:
83
- // optional, nullable, default, prefault, nonoptional, readonly, catch,
84
- // promise, lazy, success — every wrapper Zod spells `innerType`.
85
- if (def.innerType) children.push([def.innerType, path])
86
- if (def.getter) {
87
- try {
88
- children.push([def.getter(), path])
89
- } catch {
90
- // A lazy schema that cannot be forced tells us nothing; skip it.
91
- }
92
- }
93
- }
94
-
95
- for (const [child, childPath] of children) {
96
- const found = walk(child, childPath)
97
- if (found !== undefined) return found
98
- }
99
- return undefined
100
- }
101
-
102
- return walk(schema, '')
103
- }
@@ -1,48 +0,0 @@
1
- import * as ts from 'typescript'
2
-
3
- const isExportedVariableStatement = (
4
- statement: ts.Statement
5
- ): statement is ts.VariableStatement =>
6
- ts.isVariableStatement(statement) &&
7
- (statement.modifiers?.some(
8
- (modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword
9
- ) ??
10
- false)
11
-
12
- export const getExportedVariableName = (
13
- node: ts.Node,
14
- sourceFile: ts.SourceFile | undefined
15
- ): string | null => {
16
- if (!sourceFile) {
17
- return null
18
- }
19
-
20
- if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {
21
- for (const statement of sourceFile.statements) {
22
- if (!isExportedVariableStatement(statement)) continue
23
- for (const declaration of statement.declarationList.declarations) {
24
- if (declaration === node) {
25
- return node.name.text
26
- }
27
- }
28
- }
29
- }
30
-
31
- if (!ts.isCallExpression(node)) {
32
- return null
33
- }
34
-
35
- for (const statement of sourceFile.statements) {
36
- if (!isExportedVariableStatement(statement)) continue
37
- for (const declaration of statement.declarationList.declarations) {
38
- if (
39
- ts.isIdentifier(declaration.name) &&
40
- declaration.initializer === node
41
- ) {
42
- return declaration.name.text
43
- }
44
- }
45
- }
46
-
47
- return null
48
- }
@@ -1,157 +0,0 @@
1
- import type {
2
- PathToNameAndType,
3
- InspectorState,
4
- InspectorOptions,
5
- } from '../types.js'
6
-
7
- interface Meta {
8
- file: string
9
- variable: string
10
- type: string
11
- typePath: string
12
- }
13
-
14
- export type FilesAndMethods = {
15
- userSessionType: Meta
16
- wireServicesType: Meta
17
- singletonServicesType: Meta
18
- pikkuConfigType: Meta
19
- pikkuConfigFactory: Meta
20
- singletonServicesFactory: Meta
21
- wireServicesFactory: Meta
22
- serverLifecycleFactory: Meta
23
- }
24
-
25
- export type FilesAndMethodsErrors = Map<string, PathToNameAndType>
26
-
27
- const getMetaTypes = (
28
- type: string,
29
- map: PathToNameAndType,
30
- desiredType?: string,
31
- errors?: FilesAndMethodsErrors
32
- ): Meta | undefined => {
33
- if (desiredType) {
34
- for (const [file, meta] of map.entries()) {
35
- for (const { type: entryType, variable, typePath } of meta) {
36
- if (entryType === desiredType) {
37
- if (entryType === null || typePath === null) {
38
- throw new Error(
39
- `Unknown state due to metaType calculation: entryType or typePath is null for ${desiredType} in ${file}`
40
- )
41
- }
42
- return { file, variable, type: entryType, typePath }
43
- }
44
- }
45
- }
46
- if (errors) {
47
- errors.set(`No ${desiredType} found that extends ${type}`, map)
48
- }
49
- return
50
- }
51
-
52
- const totalValues = Array.from(map.values()).flat()
53
-
54
- if (totalValues.length === 0) {
55
- const helpMessage =
56
- type === 'CoreConfig'
57
- ? `No ${type} found. Make sure you have exported a createConfig function in your codebase:\n\n` +
58
- `export const createConfig = pikkuConfig(async () => {\n` +
59
- ` return {}\n` +
60
- `})\n\n` +
61
- `Possible issues:\n` +
62
- `- srcDirectories in pikku.config.json doesn't include the file with the createConfig method`
63
- : `No ${type} found`
64
- if (errors) {
65
- errors.set(helpMessage, map)
66
- }
67
- } else if (totalValues.length > 1) {
68
- if (errors) {
69
- errors.set(`More than one ${type} found`, map)
70
- }
71
- } else {
72
- const entry = Array.from(map.entries())[0]
73
- if (entry) {
74
- const [file, [{ type: entryType, variable, typePath }]] = entry
75
- if (entryType === null || typePath === null) {
76
- throw new Error(
77
- `Unknown state due to metaType calculation: entryType or typePath is null for ${type} in ${file}`
78
- )
79
- }
80
- return { file, type: entryType, variable, typePath }
81
- }
82
- }
83
-
84
- return
85
- }
86
-
87
- export const getFilesAndMethods = (
88
- {
89
- singletonServicesTypeImportMap,
90
- wireServicesTypeImportMap,
91
- userSessionTypeImportMap,
92
- configTypeImportMap,
93
- wireServicesFactories,
94
- singletonServicesFactories,
95
- configFactories,
96
- serverLifecycleFactories,
97
- }: InspectorState,
98
- {
99
- configFileType,
100
- userSessionType,
101
- singletonServicesFactoryType,
102
- wireServicesFactoryType,
103
- }: InspectorOptions['types'] = {}
104
- ): { result: Partial<FilesAndMethods>; errors: FilesAndMethodsErrors } => {
105
- const errors: FilesAndMethodsErrors = new Map()
106
-
107
- const result: Partial<FilesAndMethods> = {
108
- userSessionType: getMetaTypes(
109
- 'CoreUserSession',
110
- userSessionTypeImportMap,
111
- userSessionType,
112
- errors
113
- ),
114
- singletonServicesType: getMetaTypes(
115
- 'CoreSingletonServices',
116
- singletonServicesTypeImportMap,
117
- undefined,
118
- errors
119
- ),
120
- wireServicesType: getMetaTypes(
121
- 'CoreServices',
122
- wireServicesTypeImportMap,
123
- undefined,
124
- errors
125
- ),
126
- pikkuConfigType: getMetaTypes(
127
- 'CoreConfig',
128
- configTypeImportMap,
129
- undefined,
130
- errors
131
- ),
132
- pikkuConfigFactory: getMetaTypes(
133
- 'CoreConfig',
134
- configFactories,
135
- configFileType,
136
- errors
137
- ),
138
- singletonServicesFactory: getMetaTypes(
139
- 'CreateSingletonServices',
140
- singletonServicesFactories,
141
- singletonServicesFactoryType,
142
- errors
143
- ),
144
- wireServicesFactory: getMetaTypes(
145
- 'CreateWireServices',
146
- wireServicesFactories,
147
- wireServicesFactoryType,
148
- errors
149
- ),
150
- serverLifecycleFactory: getMetaTypes(
151
- 'ServerLifecycle',
152
- serverLifecycleFactories
153
- ),
154
- }
155
-
156
- return { result, errors }
157
- }