@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
@@ -0,0 +1,162 @@
1
+ import * as ts from 'typescript';
2
+ import { getPropertyValue } from '../utils/get-property-value.js';
3
+ import { ErrorCode } from '../error-codes.js';
4
+ const SEPARATOR = ':';
5
+ const DEFINE_FEATURE_FLAGS = 'defineFeatureFlags';
6
+ /**
7
+ * A call is the pikku one when the symbol it resolves to is, whatever the file
8
+ * chose to call it locally — `import { defineFeatureFlags as declare }` is an
9
+ * ordinary thing to write, and matching on the callee's text alone both misses
10
+ * that and claims a same-named local helper.
11
+ */
12
+ const callsDefineFeatureFlags = (expression, checker) => {
13
+ if (!ts.isIdentifier(expression))
14
+ return false;
15
+ const symbol = checker.getSymbolAtLocation(expression);
16
+ if (!symbol)
17
+ return expression.text === DEFINE_FEATURE_FLAGS;
18
+ // An import specifier names what it imported even when the module itself does
19
+ // not resolve, which is every project whose deps are not installed yet.
20
+ const declaration = symbol.declarations?.[0];
21
+ if (declaration && ts.isImportSpecifier(declaration)) {
22
+ return ((declaration.propertyName ?? declaration.name).text ===
23
+ DEFINE_FEATURE_FLAGS);
24
+ }
25
+ // Only an imported symbol can be pikku's. A local const of the same name is
26
+ // a helper the file wrote for itself, and resolving it by name alone would
27
+ // claim it.
28
+ if (!(symbol.flags & ts.SymbolFlags.Alias))
29
+ return false;
30
+ const resolved = checker.getAliasedSymbol(symbol) ?? symbol;
31
+ return resolved.name === DEFINE_FEATURE_FLAGS;
32
+ };
33
+ /**
34
+ * The object literal a declaration's value is written as, following one level
35
+ * of indirection.
36
+ *
37
+ * `{ newCheckout }` and `{ newCheckout: flag }` both read naturally with the
38
+ * body declared above, and the flag's `anyOf` is extracted by AST — so the
39
+ * literal has to be found where it was written rather than the property being
40
+ * skipped, which would declare the flag with no capability constraint at all.
41
+ */
42
+ const resolveObjectLiteral = (property, checker) => {
43
+ if (ts.isShorthandPropertyAssignment(property)) {
44
+ // The name of a shorthand resolves to the property, not to the value it
45
+ // stands for, which is what `getShorthandAssignmentValueSymbol` is for.
46
+ return fromSymbol(checker.getShorthandAssignmentValueSymbol(property), checker);
47
+ }
48
+ const unwrapped = unwrapAs(property.initializer);
49
+ if (ts.isObjectLiteralExpression(unwrapped))
50
+ return unwrapped;
51
+ if (!ts.isIdentifier(unwrapped))
52
+ return undefined;
53
+ return fromSymbol(checker.getSymbolAtLocation(unwrapped), checker);
54
+ };
55
+ const fromSymbol = (symbol, checker) => {
56
+ const resolved = symbol && symbol.flags & ts.SymbolFlags.Alias
57
+ ? (checker.getAliasedSymbol(symbol) ?? symbol)
58
+ : symbol;
59
+ for (const declaration of resolved?.declarations ?? []) {
60
+ if (!ts.isVariableDeclaration(declaration) || !declaration.initializer) {
61
+ continue;
62
+ }
63
+ const initializer = unwrapAs(declaration.initializer);
64
+ if (ts.isObjectLiteralExpression(initializer))
65
+ return initializer;
66
+ }
67
+ return undefined;
68
+ };
69
+ const unwrapAs = (node) => ts.isAsExpression(node) || ts.isSatisfiesExpression(node)
70
+ ? unwrapAs(node.expression)
71
+ : node;
72
+ /**
73
+ * Reads an optional `anyOf: [...]` array of string literals.
74
+ *
75
+ * Absent is not the same as empty: absent means no capability constraint, which
76
+ * is the shape of a pure kill switch, while an empty array satisfies nobody and
77
+ * is rejected downstream as a declaration mistake.
78
+ */
79
+ const extractAnyOf = (obj, flagName, logger) => {
80
+ const prop = obj.properties.find((p) => ts.isPropertyAssignment(p) &&
81
+ (ts.isIdentifier(p.name) || ts.isStringLiteral(p.name)) &&
82
+ p.name.text === 'anyOf');
83
+ if (!prop) {
84
+ return undefined;
85
+ }
86
+ if (!ts.isPropertyAssignment(prop) ||
87
+ !ts.isArrayLiteralExpression(prop.initializer)) {
88
+ logger.critical(ErrorCode.INVALID_VALUE, `Feature flag '${flagName}' must declare 'anyOf' as an array literal.`);
89
+ return undefined;
90
+ }
91
+ const scopes = [];
92
+ for (const element of prop.initializer.elements) {
93
+ const unwrapped = unwrapAs(element);
94
+ if (!ts.isStringLiteral(unwrapped)) {
95
+ logger.critical(ErrorCode.INVALID_VALUE, `Feature flag '${flagName}' lists a scope that is not a string literal. ` +
96
+ `Scopes are extracted by AST, so a computed value cannot be checked against the declared set.`);
97
+ continue;
98
+ }
99
+ scopes.push(unwrapped.text);
100
+ }
101
+ return scopes;
102
+ };
103
+ export const addFeatureFlag = (logger, node, checker, state, _options) => {
104
+ if (!ts.isCallExpression(node)) {
105
+ return;
106
+ }
107
+ if (!callsDefineFeatureFlags(node.expression, checker)) {
108
+ return;
109
+ }
110
+ const firstArg = node.arguments[0];
111
+ if (!firstArg) {
112
+ return;
113
+ }
114
+ const unwrapped = unwrapAs(firstArg);
115
+ if (!ts.isObjectLiteralExpression(unwrapped)) {
116
+ return;
117
+ }
118
+ const sourceFile = node.getSourceFile().fileName;
119
+ for (const prop of unwrapped.properties) {
120
+ const shorthand = ts.isShorthandPropertyAssignment(prop);
121
+ if (!shorthand && !ts.isPropertyAssignment(prop)) {
122
+ continue;
123
+ }
124
+ let name;
125
+ if (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name)) {
126
+ name = prop.name.text;
127
+ }
128
+ if (name === undefined) {
129
+ logger.critical(ErrorCode.NON_LITERAL_WIRE_NAME, 'A feature flag is declared with a key that is not a literal.');
130
+ continue;
131
+ }
132
+ if (name.length === 0) {
133
+ logger.critical(ErrorCode.INVALID_VALUE, 'A feature flag is declared with an empty name.');
134
+ continue;
135
+ }
136
+ if (name.includes(SEPARATOR)) {
137
+ logger.critical(ErrorCode.INVALID_VALUE, `Feature flag '${name}' contains the '${SEPARATOR}' separator. ` +
138
+ `'${SEPARATOR}' delimits scope ids; a flag named like a scope reads as one.`);
139
+ continue;
140
+ }
141
+ const body = resolveObjectLiteral(prop, checker);
142
+ if (!body) {
143
+ logger.critical(ErrorCode.INVALID_VALUE, `Feature flag '${name}' must be an object literal, or a variable holding one. ` +
144
+ `'anyOf' is extracted by AST, so a value this file cannot see would declare the flag with no capability constraint.`);
145
+ continue;
146
+ }
147
+ const anyOf = extractAnyOf(body, name, logger);
148
+ if (anyOf !== undefined && anyOf.length === 0) {
149
+ logger.critical(ErrorCode.INVALID_VALUE, `Feature flag '${name}' declares 'anyOf: []'. An empty list satisfies ` +
150
+ `nobody: omit 'anyOf' for a pure switch, or name the scopes that reveal it.`);
151
+ continue;
152
+ }
153
+ const description = getPropertyValue(body, 'description');
154
+ state.featureFlags.files.add(sourceFile);
155
+ state.featureFlags.definitions.push({
156
+ name,
157
+ description: description || undefined,
158
+ anyOf,
159
+ sourceFile,
160
+ });
161
+ }
162
+ };
@@ -47,6 +47,52 @@ function recordMiddlewareGroup(groups, key, group) {
47
47
  services: Array.from(new Set([...existing.services.services, ...group.services.services])),
48
48
  };
49
49
  }
50
+ /**
51
+ * Record one `add*Middleware` call's entries in the instance map, giving each
52
+ * entry an id that is unique across the whole inspection.
53
+ *
54
+ * The index used to restart at 0 for every call, so a second file registering
55
+ * against the same group (or against `global`, which is one group for the whole
56
+ * project) minted ids the first file already owned and silently overwrote its
57
+ * entries. For `global:*` that is fatal rather than cosmetic: the instance map
58
+ * is the only record of a global registration, and per-unit deploy codegen
59
+ * emits its side-effect imports from that map — a lost entry means the module
60
+ * is never imported, `addGlobalMiddleware` never runs, and an auth gate is
61
+ * simply absent from the deployed unit with nothing in the build log.
62
+ *
63
+ * Allocating from the ids already present makes N registrations against one
64
+ * group produce N distinct entries, whatever order the files are visited in.
65
+ */
66
+ function registerMiddlewareInstances(instances, groupType, groupKey, entries, sourceFile, position) {
67
+ // The inspector may revisit a file; the same call site is not a second
68
+ // registration, and re-allocating would inflate the map on every pass.
69
+ const alreadyRecorded = Object.entries(instances)
70
+ .filter(([id, instance]) => instance.sourceFile === sourceFile &&
71
+ instance.position === position &&
72
+ id.startsWith(`${makeContextBasedId(groupType, groupKey)}:`))
73
+ .map(([id]) => id);
74
+ if (alreadyRecorded.length === entries.length) {
75
+ return alreadyRecorded;
76
+ }
77
+ const instanceIds = [];
78
+ let next = 0;
79
+ for (const entry of entries) {
80
+ let instanceId = makeContextBasedId(groupType, groupKey, String(next));
81
+ while (instances[instanceId]) {
82
+ next++;
83
+ instanceId = makeContextBasedId(groupType, groupKey, String(next));
84
+ }
85
+ instances[instanceId] = {
86
+ definitionId: entry.definitionId,
87
+ sourceFile,
88
+ position,
89
+ isFactoryCall: entry.isFactoryCall,
90
+ };
91
+ instanceIds.push(instanceId);
92
+ next++;
93
+ }
94
+ return instanceIds;
95
+ }
50
96
  function renameTempDefinitions(state, definitionIds, groupType, groupKey, storeKey = 'middleware') {
51
97
  const tempIndices = definitionIds
52
98
  .map((name, i) => (name.startsWith('__temp_') ? i : -1))
@@ -237,17 +283,10 @@ export const addMiddleware = (logger, node, checker, state) => {
237
283
  renameTempDefinitions(state, definitionIds, 'tag', tag);
238
284
  }
239
285
  const sourceFile = node.getSourceFile().fileName;
240
- const instanceIds = [];
241
- for (let i = 0; i < refs.length; i++) {
242
- const instanceId = makeContextBasedId('tag', tag, String(i));
243
- state.middleware.instances[instanceId] = {
244
- definitionId: definitionIds[i],
245
- sourceFile,
246
- position: node.getStart(),
247
- isFactoryCall: refs[i].isFactoryCall,
248
- };
249
- instanceIds.push(instanceId);
250
- }
286
+ const instanceIds = registerMiddlewareInstances(state.middleware.instances, 'tag', tag, refs.map((ref, i) => ({
287
+ definitionId: definitionIds[i],
288
+ isFactoryCall: ref.isFactoryCall,
289
+ })), sourceFile, node.getStart());
251
290
  const allServices = new Set();
252
291
  for (const defId of definitionIds) {
253
292
  const def = state.middleware.definitions[defId];
@@ -309,15 +348,14 @@ export const addMiddleware = (logger, node, checker, state) => {
309
348
  renameTempDefinitions(state, definitionIds, 'global', 'middleware');
310
349
  }
311
350
  const sourceFile = node.getSourceFile().fileName;
312
- for (let i = 0; i < refs.length; i++) {
313
- const instanceId = makeContextBasedId('global', 'middleware', String(i));
314
- state.middleware.instances[instanceId] = {
315
- definitionId: definitionIds[i],
316
- sourceFile,
317
- position: node.getStart(),
318
- isFactoryCall: refs[i].isFactoryCall,
319
- };
320
- }
351
+ registerMiddlewareInstances(state.middleware.instances, 'global', 'middleware', refs.map((ref, i) => ({
352
+ definitionId: definitionIds[i],
353
+ isFactoryCall: ref.isFactoryCall,
354
+ })), sourceFile, node.getStart());
355
+ // The instance map is one map for the whole project, and codegen reads it
356
+ // by string-prefix. This set is the independent witness that lets codegen
357
+ // fail loudly if a global registration ever goes missing from it.
358
+ state.middleware.globalFiles.add(sourceFile);
321
359
  // Without this, bootstrap codegen's "import every file with a wire-call"
322
360
  // pass skips middleware-only files and the registration never runs.
323
361
  state.http.files.add(sourceFile);
@@ -347,17 +385,10 @@ export const addMiddleware = (logger, node, checker, state) => {
347
385
  renameTempDefinitions(state, definitionIds, 'http', pattern);
348
386
  }
349
387
  const sourceFile = node.getSourceFile().fileName;
350
- const instanceIds = [];
351
- for (let i = 0; i < refs.length; i++) {
352
- const instanceId = makeContextBasedId('http', pattern, String(i));
353
- state.middleware.instances[instanceId] = {
354
- definitionId: definitionIds[i],
355
- sourceFile,
356
- position: node.getStart(),
357
- isFactoryCall: refs[i].isFactoryCall,
358
- };
359
- instanceIds.push(instanceId);
360
- }
388
+ const instanceIds = registerMiddlewareInstances(state.middleware.instances, 'http', pattern, refs.map((ref, i) => ({
389
+ definitionId: definitionIds[i],
390
+ isFactoryCall: ref.isFactoryCall,
391
+ })), sourceFile, node.getStart());
361
392
  const allServices = new Set();
362
393
  for (const defId of definitionIds) {
363
394
  const def = state.middleware.definitions[defId];
@@ -581,17 +612,10 @@ export const addMiddleware = (logger, node, checker, state) => {
581
612
  const definitionIds = refs.map((r) => r.definitionId);
582
613
  renameTempDefinitions(state, definitionIds, 'tag', tag, 'channelMiddleware');
583
614
  const sourceFile = node.getSourceFile().fileName;
584
- const instanceIds = [];
585
- for (let i = 0; i < refs.length; i++) {
586
- const instanceId = makeContextBasedId('tag', tag, String(i));
587
- state.channelMiddleware.instances[instanceId] = {
588
- definitionId: definitionIds[i],
589
- sourceFile,
590
- position: node.getStart(),
591
- isFactoryCall: refs[i].isFactoryCall,
592
- };
593
- instanceIds.push(instanceId);
594
- }
615
+ const instanceIds = registerMiddlewareInstances(state.channelMiddleware.instances, 'tag', tag, refs.map((ref, i) => ({
616
+ definitionId: definitionIds[i],
617
+ isFactoryCall: ref.isFactoryCall,
618
+ })), sourceFile, node.getStart());
595
619
  const allServices = new Set();
596
620
  for (const defId of definitionIds) {
597
621
  const def = state.channelMiddleware.definitions[defId];
@@ -70,10 +70,12 @@ export function addWireAddon(node, state, logger) {
70
70
  else if (key === 'rpcEndpoint' && ts.isStringLiteral(prop.initializer)) {
71
71
  rpcEndpoint = prop.initializer.text;
72
72
  }
73
- else if (key === 'mcp' &&
74
- (prop.initializer.kind === ts.SyntaxKind.TrueKeyword ||
75
- prop.initializer.kind === ts.SyntaxKind.FalseKeyword)) {
76
- mcp = prop.initializer.kind === ts.SyntaxKind.TrueKeyword;
73
+ else if (key === 'mcp') {
74
+ mcp =
75
+ prop.initializer.kind === ts.SyntaxKind.TrueKeyword ||
76
+ prop.initializer.kind === ts.SyntaxKind.FalseKeyword
77
+ ? prop.initializer.kind === ts.SyntaxKind.TrueKeyword
78
+ : parseStringArray(prop.initializer);
77
79
  }
78
80
  else if (key === 'auth' &&
79
81
  (prop.initializer.kind === ts.SyntaxKind.TrueKeyword ||
@@ -122,6 +124,7 @@ export function addWireAddon(node, state, logger) {
122
124
  logger.debug(`• Found wireAddon: ${name} → ${pkg}`);
123
125
  state.rpc.wireAddonDeclarations.set(name, {
124
126
  package: pkg,
127
+ file: node.getSourceFile().fileName,
125
128
  rpcEndpoint,
126
129
  mcp,
127
130
  auth,
@@ -57,6 +57,7 @@ export function addWireRemoteAddon(node, state, logger) {
57
57
  logger.debug(`• Found wireRemoteAddon: ${name} → ${pkg} (remote)`);
58
58
  state.rpc.wireAddonDeclarations.set(name, {
59
59
  package: pkg,
60
+ file: node.getSourceFile().fileName,
60
61
  remote: true,
61
62
  hasAuth,
62
63
  authCredentialId,
@@ -32,6 +32,7 @@ export declare enum ErrorCode {
32
32
  REMOTE_ADDON_NOT_DEV_DEPENDENCY = "PKU338",
33
33
  REMOTE_ADDON_AUTH_UNRESOLVED = "PKU339",
34
34
  ADDON_NOT_INSTALLED = "PKU340",
35
+ ADDON_MCP_FUNCTION_NOT_FOUND = "PKU341",
35
36
  DB_COLUMN_NAME_TYPE_CONTRADICTION = "PKU480",
36
37
  DB_JSON_COLUMN_UNTYPED = "PKU481",
37
38
  DB_FORMAT_ON_NON_STRING = "PKU482",
@@ -35,6 +35,7 @@ export var ErrorCode;
35
35
  ErrorCode["REMOTE_ADDON_NOT_DEV_DEPENDENCY"] = "PKU338";
36
36
  ErrorCode["REMOTE_ADDON_AUTH_UNRESOLVED"] = "PKU339";
37
37
  ErrorCode["ADDON_NOT_INSTALLED"] = "PKU340";
38
+ ErrorCode["ADDON_MCP_FUNCTION_NOT_FOUND"] = "PKU341";
38
39
  // Database schema codegen warnings
39
40
  ErrorCode["DB_COLUMN_NAME_TYPE_CONTRADICTION"] = "PKU480";
40
41
  ErrorCode["DB_JSON_COLUMN_UNTYPED"] = "PKU481";
package/dist/index.d.ts CHANGED
@@ -10,7 +10,7 @@ export type { ScenarioInstrumentationFunction } from './add/scenario-instrumenta
10
10
  export { serializeInspectorState, deserializeInspectorState, } from './utils/serialize-inspector-state.js';
11
11
  export type { SerializableInspectorState } from './utils/serialize-inspector-state.js';
12
12
  export { filterInspectorState } from './utils/filter-inspector-state.js';
13
- export { resolveDeployTarget } from './utils/resolve-deploy-target.js';
13
+ export { incompatibleServicesFor, resolveDeployTarget, } from './utils/resolve-deploy-target.js';
14
14
  export { generateCustomTypes, sanitizeTypeName, } from './utils/custom-types-generator.js';
15
15
  export { unresolvedSchemaReferences } from './utils/post-process.js';
16
16
  export type { SurfaceUsageCounts } from './utils/extract-surface-usage.js';
@@ -24,3 +24,5 @@ export { getFilesAndMethods } from './utils/get-files-and-methods.js';
24
24
  export { resolveFunctionMeta } from './utils/resolve-function-meta.js';
25
25
  export type { SerializedWorkflowGraph, SerializedWorkflowGraphs, } from './utils/workflow/graph/index.js';
26
26
  export { resolveCoreType } from './utils/resolve-core-type.js';
27
+ export { readModuleSpecifiers } from './utils/read-module-specifiers.js';
28
+ export { readTsconfigOutDir } from './utils/read-tsconfig-out-dir.js';
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ export { AUTH_HANDLER_FUNC_ID } from './add/add-auth.js';
4
4
  export { SCENARIO_INSTRUMENTATION_FUNCTIONS, isScenarioInstrumentationFunction, } from './add/scenario-instrumentation.js';
5
5
  export { serializeInspectorState, deserializeInspectorState, } from './utils/serialize-inspector-state.js';
6
6
  export { filterInspectorState } from './utils/filter-inspector-state.js';
7
- export { resolveDeployTarget } from './utils/resolve-deploy-target.js';
7
+ export { incompatibleServicesFor, resolveDeployTarget, } from './utils/resolve-deploy-target.js';
8
8
  export { generateCustomTypes, sanitizeTypeName, } from './utils/custom-types-generator.js';
9
9
  export { unresolvedSchemaReferences } from './utils/post-process.js';
10
10
  export { createEmptyManifest, serializeManifest, } from './utils/contract-hashes.js';
@@ -13,3 +13,5 @@ export { deserializeDslWorkflow, deserializeGraphWorkflow, deserializeAllDslWork
13
13
  export { getFilesAndMethods } from './utils/get-files-and-methods.js';
14
14
  export { resolveFunctionMeta } from './utils/resolve-function-meta.js';
15
15
  export { resolveCoreType } from './utils/resolve-core-type.js';
16
+ export { readModuleSpecifiers } from './utils/read-module-specifiers.js';
17
+ export { readTsconfigOutDir } from './utils/read-tsconfig-out-dir.js';
package/dist/inspector.js CHANGED
@@ -160,6 +160,10 @@ export function getInitialInspectorState(rootDir) {
160
160
  definitions: [],
161
161
  files: new Set(),
162
162
  },
163
+ featureFlags: {
164
+ definitions: [],
165
+ files: new Set(),
166
+ },
163
167
  personas: {
164
168
  definitions: [],
165
169
  files: new Set(),
@@ -177,6 +181,7 @@ export function getInitialInspectorState(rootDir) {
177
181
  definitions: {},
178
182
  instances: {},
179
183
  tagMiddleware: new Map(),
184
+ globalFiles: new Set(),
180
185
  },
181
186
  channelMiddleware: {
182
187
  definitions: {},
@@ -0,0 +1,58 @@
1
+ export type SurfaceKind = 'function' | 'class' | 'interface' | 'type' | 'const' | 'enum' | 'namespace';
2
+ export type SurfaceSymbol = {
3
+ name: string;
4
+ kind: SurfaceKind;
5
+ declaredAt: string;
6
+ /** Absolute path of the declaring file, or null when it could not be found. */
7
+ declaredIn: string | null;
8
+ deprecated: boolean;
9
+ /** The text of the `@deprecated` tag, when it carries one. */
10
+ deprecatedReason?: string;
11
+ /** First paragraph of the symbol's JSDoc. */
12
+ summary?: string;
13
+ /** The symbol's JSDoc in full, paragraphs and examples included. */
14
+ docs?: string;
15
+ /** The type as the checker prints it, so a caller knows how to call it. */
16
+ signature?: string;
17
+ /**
18
+ * The shape written at the call site, one `name?: type` per entry — the
19
+ * properties of the object a function takes, or of the type itself. The
20
+ * signature names that shape; this says what is in it.
21
+ */
22
+ members?: SurfaceMember[];
23
+ /** The `@example` blocks, which the documentation comment leaves out. */
24
+ examples?: string[];
25
+ /** The HTTP status an error class is registered with. */
26
+ status?: number;
27
+ };
28
+ export type SurfaceEntrypoint = {
29
+ subpath: string;
30
+ specifier: string;
31
+ entryFile: string;
32
+ symbols: SurfaceSymbol[];
33
+ };
34
+ export type CollectSurfaceOptions = {
35
+ /**
36
+ * Read the package's `imports` map instead of its `exports` map, taking the
37
+ * one key given — `#pikku/*` is what an application reaches its generated
38
+ * leaves through, and it is a private specifier rather than a published one.
39
+ */
40
+ importsSubpath?: string;
41
+ /** Restrict collection to these subpaths, so the program stays small. */
42
+ subpaths?: string[];
43
+ /** The snippet regions an `@example snippet: name` resolves against. */
44
+ snippets?: Map<string, string>;
45
+ };
46
+ export declare class UnknownSnippetError extends Error {
47
+ constructor(name: string, symbol: string, known: string[]);
48
+ }
49
+ /**
50
+ * One key of an options object: how it is written, and — where the declaring
51
+ * type says so — what it means. `schedule: string` is a shape; "a cron
52
+ * expression" is what a caller actually needs, and only the JSDoc has it.
53
+ */
54
+ export type SurfaceMember = {
55
+ line: string;
56
+ doc?: string;
57
+ };
58
+ export declare const collectSurface: (packageDir: string, { importsSubpath, subpaths, snippets }?: CollectSurfaceOptions) => Promise<SurfaceEntrypoint[]>;