@pikku/inspector 0.12.74 → 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 +347 -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 +23 -13
  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 -2207
  142. package/src/utils/filter-inspector-state.ts +0 -1302
  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,387 +0,0 @@
1
- import type { FunctionsMeta } from '@pikku/core/services'
2
- import type { JSONValue } from '@pikku/core/utils'
3
- import { parseVersionedId } from '@pikku/core/utils'
4
- import type { TypesMap } from '../types-map.js'
5
- import { ErrorCode } from '../error-codes.js'
6
- import { canonicalJSON, hashString } from './hash.js'
7
-
8
- export type ContractEntry = {
9
- functionKey: string
10
- version: number
11
- contractHash: string
12
- inputHash: string
13
- outputHash: string
14
- }
15
-
16
- export type VersionValidateError = {
17
- code: ErrorCode
18
- message: string
19
- functionKey?: string
20
- version?: number
21
- previousHash?: string
22
- currentHash?: string
23
- previousInputHash?: string
24
- currentInputHash?: string
25
- previousOutputHash?: string
26
- currentOutputHash?: string
27
- nextVersion?: number
28
- latestVersion?: number
29
- expectedNextVersion?: number
30
- }
31
-
32
- export type VersionHashEntry = {
33
- inputHash: string
34
- outputHash: string
35
- }
36
-
37
- export type VersionManifestEntry = {
38
- latest: number
39
- versions: Record<string, string | VersionHashEntry>
40
- }
41
-
42
- export type VersionManifest = {
43
- manifestVersion: 1
44
- contracts: Record<string, VersionManifestEntry>
45
- }
46
-
47
- function isHashEntry(v: string | VersionHashEntry): v is VersionHashEntry {
48
- return typeof v === 'object'
49
- }
50
-
51
- export function createEmptyManifest(): VersionManifest {
52
- return {
53
- manifestVersion: 1,
54
- contracts: {},
55
- }
56
- }
57
-
58
- export function serializeManifest(manifest: VersionManifest): string {
59
- const sortedContracts: Record<string, VersionManifestEntry> = {}
60
-
61
- for (const key of Object.keys(manifest.contracts).sort()) {
62
- const entry = manifest.contracts[key]
63
- const sortedVersions: Record<string, string | VersionHashEntry> = {}
64
- const numericKeys = Object.keys(entry.versions)
65
- .map(Number)
66
- .sort((a, b) => a - b)
67
- for (const vk of numericKeys) {
68
- sortedVersions[String(vk)] = entry.versions[String(vk)]
69
- }
70
- sortedContracts[key] = {
71
- latest: entry.latest,
72
- versions: sortedVersions,
73
- }
74
- }
75
-
76
- const sorted: VersionManifest = {
77
- manifestVersion: manifest.manifestVersion,
78
- contracts: sortedContracts,
79
- }
80
-
81
- return JSON.stringify(sorted, null, 2) + '\n'
82
- }
83
-
84
- export function computeContractHash(data: {
85
- functionKey: string
86
- inputSchema: unknown
87
- outputSchema: unknown
88
- }): string {
89
- return hashString(canonicalJSON(data), 16)
90
- }
91
-
92
- function computeInputHash(functionKey: string, inputSchema: unknown): string {
93
- return hashString(canonicalJSON({ functionKey, inputSchema }), 8)
94
- }
95
-
96
- function computeOutputHash(functionKey: string, outputSchema: unknown): string {
97
- return hashString(canonicalJSON({ functionKey, outputSchema }), 8)
98
- }
99
-
100
- function resolveSchema(
101
- typeNames: string[] | null | undefined,
102
- allSchemas: Record<string, JSONValue>,
103
- typesMap: TypesMap
104
- ): unknown {
105
- if (!typeNames) {
106
- return null
107
- }
108
- const filtered = typeNames.filter((n) => n !== 'void')
109
- if (filtered.length === 0) {
110
- return null
111
- }
112
- const parts: unknown[] = []
113
- for (const name of filtered) {
114
- let key: string
115
- try {
116
- key = typesMap.getUniqueName(name)
117
- } catch {
118
- key = name
119
- }
120
- const schema = allSchemas[key]
121
- if (schema) {
122
- parts.push(schema)
123
- }
124
- }
125
- if (parts.length === 0) {
126
- return null
127
- }
128
- return parts.length === 1 ? parts[0] : parts
129
- }
130
-
131
- export function buildCurrentContracts(
132
- functionsMeta: FunctionsMeta,
133
- allSchemas: Record<string, JSONValue>,
134
- typesMap: TypesMap
135
- ): Map<string, ContractEntry> {
136
- const result = new Map<string, ContractEntry>()
137
-
138
- for (const [funcId, meta] of Object.entries(functionsMeta)) {
139
- if (meta.remote === true) {
140
- continue
141
- }
142
-
143
- const parsed = parseVersionedId(funcId)
144
- const functionKey = parsed.baseName
145
- const version = parsed.version ?? meta.version ?? 1
146
-
147
- const inputSchema = resolveSchema(meta.inputs, allSchemas, typesMap)
148
- const outputSchema = resolveSchema(meta.outputs, allSchemas, typesMap)
149
-
150
- const contractHash = computeContractHash({
151
- functionKey,
152
- inputSchema,
153
- outputSchema,
154
- })
155
- const inputHash = computeInputHash(functionKey, inputSchema)
156
- const outputHash = computeOutputHash(functionKey, outputSchema)
157
-
158
- result.set(funcId, {
159
- functionKey,
160
- version,
161
- contractHash,
162
- inputHash,
163
- outputHash,
164
- })
165
- }
166
-
167
- return result
168
- }
169
-
170
- export function computeContractHashes(
171
- allSchemas: Record<string, JSONValue>,
172
- typesMap: TypesMap,
173
- functionsMeta: FunctionsMeta
174
- ): Map<string, ContractEntry> {
175
- const contracts = buildCurrentContracts(functionsMeta, allSchemas, typesMap)
176
-
177
- for (const [funcId, entry] of contracts) {
178
- const meta = functionsMeta[funcId]
179
- if (meta) {
180
- meta.contractHash = entry.contractHash
181
- meta.inputHash = entry.inputHash
182
- meta.outputHash = entry.outputHash
183
- }
184
- }
185
-
186
- return contracts
187
- }
188
-
189
- function groupByFunctionKey(
190
- contracts: Map<string, ContractEntry>
191
- ): Map<string, ContractEntry[]> {
192
- const grouped = new Map<string, ContractEntry[]>()
193
- for (const entry of contracts.values()) {
194
- const existing = grouped.get(entry.functionKey) ?? []
195
- existing.push(entry)
196
- grouped.set(entry.functionKey, existing)
197
- }
198
- return grouped
199
- }
200
-
201
- function entryChanged(
202
- existing: string | VersionHashEntry,
203
- current: ContractEntry
204
- ): boolean {
205
- if (isHashEntry(existing)) {
206
- return (
207
- existing.inputHash !== current.inputHash ||
208
- existing.outputHash !== current.outputHash
209
- )
210
- }
211
- return existing !== current.contractHash
212
- }
213
-
214
- export function validateContracts(
215
- manifest: VersionManifest,
216
- currentContracts: Map<string, ContractEntry>
217
- ): { valid: boolean; errors: VersionValidateError[] } {
218
- const errors: VersionValidateError[] = []
219
- const grouped = groupByFunctionKey(currentContracts)
220
- const reportedKeys = new Set<string>()
221
-
222
- for (const [functionKey, entries] of grouped) {
223
- const manifestEntry = manifest.contracts[functionKey]
224
- if (!manifestEntry) {
225
- continue
226
- }
227
-
228
- for (const current of entries) {
229
- const { version, contractHash, inputHash, outputHash } = current
230
- const existingEntry = manifestEntry.versions[String(version)]
231
-
232
- if (existingEntry !== undefined) {
233
- if (entryChanged(existingEntry, current)) {
234
- reportedKeys.add(`${functionKey}@${version}`)
235
- const prevInputHash = isHashEntry(existingEntry)
236
- ? existingEntry.inputHash
237
- : existingEntry
238
- const prevOutputHash = isHashEntry(existingEntry)
239
- ? existingEntry.outputHash
240
- : existingEntry
241
- errors.push({
242
- code: ErrorCode.FUNCTION_VERSION_MODIFIED,
243
- message: `Contract for ${functionKey}@v${version} has changed (recorded: ${isHashEntry(existingEntry) ? `${existingEntry.inputHash}/${existingEntry.outputHash}` : existingEntry}, current: ${contractHash}). Existing versions are immutable.`,
244
- functionKey,
245
- version,
246
- previousHash: isHashEntry(existingEntry)
247
- ? existingEntry.inputHash
248
- : existingEntry,
249
- currentHash: contractHash,
250
- previousInputHash: prevInputHash,
251
- currentInputHash: inputHash,
252
- previousOutputHash: prevOutputHash,
253
- currentOutputHash: outputHash,
254
- })
255
- }
256
- } else {
257
- if (version <= manifestEntry.latest) {
258
- errors.push({
259
- code: ErrorCode.VERSION_REGRESSION_OR_CONFLICT,
260
- message: `Version ${version} for ${functionKey} is <= latest (${manifestEntry.latest}) but not recorded. Possible merge conflict.`,
261
- functionKey,
262
- version,
263
- latestVersion: manifestEntry.latest,
264
- })
265
- } else if (version > manifestEntry.latest + 1) {
266
- errors.push({
267
- code: ErrorCode.VERSION_GAP_NOT_ALLOWED,
268
- message: `Version ${version} for ${functionKey} skips versions. Latest is ${manifestEntry.latest}, next must be ${manifestEntry.latest + 1}.`,
269
- functionKey,
270
- version,
271
- latestVersion: manifestEntry.latest,
272
- expectedNextVersion: manifestEntry.latest + 1,
273
- })
274
- }
275
- }
276
- }
277
- }
278
-
279
- for (const [functionKey, manifestEntry] of Object.entries(
280
- manifest.contracts
281
- )) {
282
- const latestEntry = manifestEntry.versions[String(manifestEntry.latest)]
283
- const currentEntries = grouped.get(functionKey)
284
- if (!currentEntries) {
285
- continue
286
- }
287
-
288
- const currentLatest = currentEntries.find(
289
- (e) => e.version === manifestEntry.latest
290
- )
291
- if (
292
- currentLatest &&
293
- entryChanged(latestEntry, currentLatest) &&
294
- !reportedKeys.has(`${functionKey}@${manifestEntry.latest}`)
295
- ) {
296
- const prevInputHash = isHashEntry(latestEntry)
297
- ? latestEntry.inputHash
298
- : undefined
299
- const prevOutputHash = isHashEntry(latestEntry)
300
- ? latestEntry.outputHash
301
- : undefined
302
- errors.push({
303
- code: ErrorCode.CONTRACT_CHANGED_REQUIRES_BUMP,
304
- message: `Contract for ${functionKey} changed. Set \`version: ${manifestEntry.latest + 1}\` on the function or run 'pikku versions update'.`,
305
- functionKey,
306
- previousInputHash: prevInputHash,
307
- currentInputHash: currentLatest.inputHash,
308
- previousOutputHash: prevOutputHash,
309
- currentOutputHash: currentLatest.outputHash,
310
- nextVersion: manifestEntry.latest + 1,
311
- })
312
- }
313
- }
314
-
315
- for (const [functionKey, manifestEntry] of Object.entries(
316
- manifest.contracts
317
- )) {
318
- const numericKeys = Object.keys(manifestEntry.versions).map(Number)
319
- if (numericKeys.length === 0) {
320
- continue
321
- }
322
- const maxVersion = Math.max(...numericKeys)
323
- if (manifestEntry.latest !== maxVersion) {
324
- errors.push({
325
- code: ErrorCode.MANIFEST_INTEGRITY_ERROR,
326
- message: `Manifest integrity error for ${functionKey}: latest field (${manifestEntry.latest}) inconsistent with version keys (max: ${maxVersion}).`,
327
- functionKey,
328
- latestVersion: manifestEntry.latest,
329
- expectedNextVersion: maxVersion,
330
- })
331
- }
332
- }
333
-
334
- return { valid: errors.length === 0, errors }
335
- }
336
-
337
- export function updateManifest(
338
- existing: VersionManifest,
339
- currentContracts: Map<string, ContractEntry>
340
- ): VersionManifest {
341
- const manifest: VersionManifest = {
342
- manifestVersion: existing.manifestVersion,
343
- contracts: JSON.parse(JSON.stringify(existing.contracts)),
344
- }
345
-
346
- const grouped = groupByFunctionKey(currentContracts)
347
-
348
- for (const [functionKey, entries] of grouped) {
349
- if (!manifest.contracts[functionKey]) {
350
- manifest.contracts[functionKey] = { latest: 0, versions: {} }
351
- }
352
-
353
- const entry = manifest.contracts[functionKey]
354
- for (const { version, inputHash, outputHash } of entries) {
355
- entry.versions[String(version)] = { inputHash, outputHash }
356
- entry.latest = Math.max(entry.latest, version)
357
- }
358
- }
359
-
360
- return manifest
361
- }
362
-
363
- export function extractContractsFromMeta(
364
- functionsMeta: FunctionsMeta
365
- ): Map<string, ContractEntry> {
366
- const result = new Map<string, ContractEntry>()
367
-
368
- for (const [funcId, meta] of Object.entries(functionsMeta)) {
369
- if (meta.remote === true || !meta.contractHash) {
370
- continue
371
- }
372
-
373
- const parsed = parseVersionedId(funcId)
374
- const functionKey = parsed.baseName
375
- const version = parsed.version ?? meta.version ?? 1
376
-
377
- result.set(funcId, {
378
- functionKey,
379
- version,
380
- contractHash: meta.contractHash,
381
- inputHash: meta.inputHash ?? '',
382
- outputHash: meta.outputHash ?? '',
383
- })
384
- }
385
-
386
- return result
387
- }
@@ -1,124 +0,0 @@
1
- import { test, describe } from 'node:test'
2
- import { strict as assert } from 'node:assert'
3
- import {
4
- generateCustomTypes,
5
- sanitizeTypeName,
6
- } from './custom-types-generator.js'
7
- import { TypesMap } from '../types-map.js'
8
-
9
- function makeTypesMap(
10
- entries: Record<string, { type: string; references: string[] }>
11
- ): TypesMap {
12
- const tm = new TypesMap()
13
- for (const [name, { type, references }] of Object.entries(entries)) {
14
- tm.addCustomType(name, type, references)
15
- }
16
- return tm
17
- }
18
-
19
- describe('generateCustomTypes — classification wrapper stripping', () => {
20
- test('strips Private<T> wrapper from type alias', () => {
21
- const tm = makeTypesMap({
22
- UserEmail: { type: 'Private<string>', references: ['Private'] },
23
- })
24
- const result = generateCustomTypes(tm, new Set())
25
- assert.match(result, /UserEmail/, 'should emit UserEmail alias')
26
- assert.match(
27
- result,
28
- /= string/,
29
- 'Private<string> should be stripped to string'
30
- )
31
- assert.doesNotMatch(result, /Private/, 'Private wrapper must be removed')
32
- })
33
-
34
- test('strips Secret<T> wrapper from type alias', () => {
35
- const tm = makeTypesMap({
36
- HashedPw: { type: 'Secret<string>', references: ['Secret'] },
37
- })
38
- const result = generateCustomTypes(tm, new Set())
39
- assert.match(result, /HashedPw/)
40
- assert.match(
41
- result,
42
- /= string/,
43
- 'Secret<string> should be stripped to string'
44
- )
45
- assert.doesNotMatch(result, /Secret/, 'Secret wrapper must be removed')
46
- })
47
-
48
- test('strips Pii<T> wrapper from type alias', () => {
49
- const tm = makeTypesMap({
50
- UserPhone: { type: 'Pii<string>', references: ['Pii'] },
51
- })
52
- const result = generateCustomTypes(tm, new Set())
53
- assert.match(result, /UserPhone/)
54
- assert.match(result, /= string/, 'Pii<string> should be stripped to string')
55
- assert.doesNotMatch(result, /Pii/, 'Pii wrapper must be removed')
56
- })
57
-
58
- test('strips nested classification wrappers', () => {
59
- const tm = makeTypesMap({
60
- Combo: {
61
- type: 'Private<Secret<string>>',
62
- references: ['Private', 'Secret'],
63
- },
64
- })
65
- const result = generateCustomTypes(tm, new Set())
66
- assert.match(result, /Combo/)
67
- assert.match(
68
- result,
69
- /= string/,
70
- 'nested wrappers should resolve to inner type'
71
- )
72
- assert.doesNotMatch(
73
- result,
74
- /Private|Secret/,
75
- 'all wrappers must be removed'
76
- )
77
- })
78
-
79
- test('does not strip non-classification type names', () => {
80
- const tm = makeTypesMap({
81
- MyType: { type: 'SomeOtherType', references: ['SomeOtherType'] },
82
- })
83
- const required = new Set<string>()
84
- generateCustomTypes(tm, required)
85
- assert.ok(
86
- required.has('SomeOtherType'),
87
- 'non-classification references must remain in requiredTypes'
88
- )
89
- })
90
-
91
- test('classification wrapper references are not added to requiredTypes', () => {
92
- const tm = makeTypesMap({
93
- SensitiveField: { type: 'Private<string>', references: ['Private'] },
94
- })
95
- const required = new Set<string>()
96
- generateCustomTypes(tm, required)
97
- assert.ok(
98
- !required.has('Private'),
99
- 'Private must not be added to requiredTypes'
100
- )
101
- })
102
- })
103
-
104
- describe('sanitizeTypeName — valid JS identifier', () => {
105
- test('replaces disallowed characters with underscores', () => {
106
- assert.equal(
107
- sanitizeTypeName('Add Beehiiv subscribers'),
108
- 'Add_Beehiiv_subscribers'
109
- )
110
- })
111
-
112
- test('prefixes an underscore when the name starts with a digit', () => {
113
- // A name like "2. Add Beehiiv newsletter subscribers" otherwise produces
114
- // `2__Add_...`, an invalid JS identifier that breaks the generated
115
- // `import <ident> from ...` in pikku-workflow-wirings-meta.gen.ts.
116
- const id = sanitizeTypeName('2. Add Beehiiv newsletter subscribers')
117
- assert.equal(id, '_2__Add_Beehiiv_newsletter_subscribers')
118
- assert.match(id, /^[A-Za-z_$]/, 'must start with a valid identifier char')
119
- })
120
-
121
- test('leaves an already-valid identifier unchanged', () => {
122
- assert.equal(sanitizeTypeName('UserEmail'), 'UserEmail')
123
- })
124
- })
@@ -1,150 +0,0 @@
1
- import type { TypesMap } from '../types-map.js'
2
-
3
- /**
4
- * NOTE: Code generation normally belongs in @pikku/cli, not the inspector.
5
- * This is here because the schema generator needs the custom types content
6
- * as a virtual TypeScript source file (in-memory, no disk write) so that
7
- * ts-json-schema-generator can discover inline/custom types from typesMap.
8
- */
9
-
10
- export function sanitizeTypeName(name: string): string {
11
- const sanitized = name.replace(/[^a-zA-Z0-9_$]/g, '_')
12
- return /^[0-9]/.test(sanitized) ? `_${sanitized}` : sanitized
13
- }
14
-
15
- const CLASSIFICATION_WRAPPERS = new Set(['Private', 'Pii', 'Secret'])
16
-
17
- function findMatchingAngleBracket(type: string, startIndex: number): number {
18
- let depth = 0
19
- for (let i = startIndex; i < type.length; i += 1) {
20
- const char = type[i]
21
- if (char === '<') {
22
- depth += 1
23
- continue
24
- }
25
- if (char === '>' && (i === 0 || type[i - 1] !== '=')) {
26
- depth -= 1
27
- if (depth === 0) {
28
- return i
29
- }
30
- }
31
- }
32
- return -1
33
- }
34
-
35
- function stripClassificationWrappers(type: string): string {
36
- let output = ''
37
- let index = 0
38
-
39
- while (index < type.length) {
40
- const char = type[index]
41
- if (!/[A-Za-z_$]/.test(char)) {
42
- output += char
43
- index += 1
44
- continue
45
- }
46
-
47
- let end = index + 1
48
- while (end < type.length && /[A-Za-z0-9_$]/.test(type[end])) {
49
- end += 1
50
- }
51
-
52
- const identifier = type.slice(index, end)
53
- if (CLASSIFICATION_WRAPPERS.has(identifier) && type[end] === '<') {
54
- const closingIndex = findMatchingAngleBracket(type, end)
55
- if (closingIndex !== -1) {
56
- const inner = type.slice(end + 1, closingIndex)
57
- output += stripClassificationWrappers(inner)
58
- index = closingIndex + 1
59
- continue
60
- }
61
- }
62
-
63
- output += identifier
64
- index = end
65
- }
66
-
67
- return output
68
- }
69
-
70
- export function generateCustomTypes(
71
- typesMap: TypesMap,
72
- requiredTypes: Set<string>
73
- ) {
74
- const typeDeclarations = Array.from(typesMap.customTypes.entries())
75
- .sort(([a], [b]) => a.localeCompare(b))
76
- .filter(([_name, { type }]) => {
77
- const hasUndefinedGeneric =
78
- /\b(Name|In|Out|Key)\b/.test(type) && /\[.*\]/.test(type)
79
- return !hasUndefinedGeneric
80
- })
81
- .map(([originalName, { type, references }]) => {
82
- const name = sanitizeTypeName(originalName)
83
- references.forEach((refName) => {
84
- if (
85
- refName !== '__object' &&
86
- !refName.startsWith('__object_') &&
87
- !CLASSIFICATION_WRAPPERS.has(refName)
88
- ) {
89
- requiredTypes.add(refName)
90
- }
91
- })
92
-
93
- const typeString = stripClassificationWrappers(type)
94
- const typeNameRegex = /\b[A-Z][a-zA-Z0-9]*\b/g
95
- const potentialTypes = typeString.match(typeNameRegex) || []
96
-
97
- potentialTypes.forEach((typeName) => {
98
- if (
99
- typeString.includes(`"${typeName}"`) ||
100
- [
101
- 'Pick',
102
- 'Omit',
103
- 'Partial',
104
- 'Required',
105
- 'Record',
106
- 'Readonly',
107
- ].includes(typeName)
108
- ) {
109
- return
110
- }
111
-
112
- try {
113
- const typeMeta = typesMap.getTypeMeta(typeName)
114
- if (typeMeta.path) {
115
- requiredTypes.add(typeMeta.originalName)
116
- }
117
- } catch {
118
- // Type not found in map (ambient/builtin type)
119
- }
120
- })
121
-
122
- if (name === typeString) return null
123
- return `export type ${name} = ${typeString}`
124
- })
125
-
126
- const importsByPath = new Map<string, Set<string>>()
127
- for (const typeName of requiredTypes) {
128
- if (CLASSIFICATION_WRAPPERS.has(typeName)) continue
129
- try {
130
- const typeMeta = typesMap.getTypeMeta(typeName)
131
- if (typeMeta.path) {
132
- if (!importsByPath.has(typeMeta.path)) {
133
- importsByPath.set(typeMeta.path, new Set())
134
- }
135
- importsByPath.get(typeMeta.path)!.add(typeMeta.originalName)
136
- }
137
- } catch {
138
- // Type not found in map
139
- }
140
- }
141
-
142
- const importLines = Array.from(importsByPath.entries())
143
- .map(
144
- ([path, types]) =>
145
- `import type { ${Array.from(types).join(', ')} } from '${path}'`
146
- )
147
- .join('\n')
148
-
149
- return `${importLines}\n\n${typeDeclarations.filter(Boolean).join('\n')}`
150
- }