@pikku/inspector 0.12.59 → 0.12.61
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.
- package/CHANGELOG.md +373 -0
- package/OPTIMIZATION-PLAN.md +19 -19
- package/dist/add/add-agent-scorer.d.ts +2 -0
- package/dist/add/{add-ai-scorer.js → add-agent-scorer.js} +4 -4
- package/dist/add/{add-ai-agent.d.ts → add-agent.d.ts} +1 -1
- package/dist/add/{add-ai-agent.js → add-agent.js} +11 -11
- package/dist/add/add-auth.js +1 -1
- package/dist/add/add-cli.js +5 -0
- package/dist/add/add-functions.js +10 -1
- package/dist/add/add-http-route.js +18 -18
- package/dist/add/add-keyed-wiring.d.ts +2 -0
- package/dist/add/add-keyed-wiring.js +3 -1
- package/dist/add/add-middleware.js +5 -5
- package/dist/add/add-personas.js +3 -0
- package/dist/add/add-secret.js +1 -0
- package/dist/add/add-variable.js +1 -0
- package/dist/add/add-wire-remote-addon.js +2 -1
- package/dist/add/add-workflow-graph.js +63 -6
- package/dist/error-codes.d.ts +3 -1
- package/dist/error-codes.js +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/inspector.js +13 -3
- package/dist/types.d.ts +10 -5
- package/dist/utils/annotate-http-route-auth.js +2 -1
- package/dist/utils/check-secret-sinks.js +2 -1
- package/dist/utils/compute-required-schemas.d.ts +1 -1
- package/dist/utils/contract-hashes.d.ts +2 -1
- package/dist/utils/contract-hashes.js +1 -1
- package/dist/utils/extract-function-name.js +1 -1
- package/dist/utils/extract-services.d.ts +1 -1
- package/dist/utils/extract-surface-usage.d.ts +29 -0
- package/dist/utils/extract-surface-usage.js +97 -0
- package/dist/utils/filter-inspector-state.js +53 -16
- package/dist/utils/filter-utils.d.ts +1 -1
- package/dist/utils/get-property-value.js +42 -3
- package/dist/utils/load-addon-functions-meta.js +31 -0
- package/dist/utils/middleware.d.ts +2 -2
- package/dist/utils/middleware.js +5 -5
- package/dist/utils/permissions.d.ts +1 -1
- package/dist/utils/post-process.d.ts +28 -8
- package/dist/utils/post-process.js +83 -18
- package/dist/utils/resolve-deploy-target.d.ts +1 -1
- package/dist/utils/resolve-function-meta.d.ts +1 -1
- package/dist/utils/resolve-function-types.d.ts +1 -1
- package/dist/utils/resolve-versions.js +1 -1
- package/dist/utils/schema-generator.d.ts +1 -1
- package/dist/utils/serialize-inspector-state.d.ts +4 -3
- package/dist/utils/serialize-inspector-state.js +6 -4
- package/dist/utils/serialize-openapi-json.d.ts +1 -1
- package/dist/utils/validate-auth-sessionless.js +2 -1
- package/dist/utils/workflow/graph/finalize-workflows.js +1 -1
- package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +6 -1
- package/dist/utils/workflow/graph/serialize-workflow-graph.js +26 -2
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +9 -0
- package/dist/visit.js +4 -4
- package/package.json +10 -10
- package/run-tests.sh +0 -0
- package/src/add/{add-ai-scorer.test.ts → add-agent-scorer.test.ts} +11 -11
- package/src/add/{add-ai-scorer.ts → add-agent-scorer.ts} +4 -4
- package/src/add/{add-ai-agent.test.ts → add-agent.test.ts} +1 -1
- package/src/add/{add-ai-agent.ts → add-agent.ts} +13 -21
- package/src/add/add-auth.ts +2 -2
- package/src/add/add-cli.ts +6 -0
- package/src/add/add-credential.ts +3 -6
- package/src/add/add-file-with-factory.ts +2 -6
- package/src/add/add-functions.ts +23 -25
- package/src/add/add-http-route-ref.test.ts +149 -0
- package/src/add/add-http-route.ts +25 -28
- package/src/add/add-keyed-wiring.ts +8 -7
- package/src/add/add-middleware.ts +7 -5
- package/src/add/add-permission.ts +1 -1
- package/src/add/add-personas.test.ts +24 -0
- package/src/add/add-personas.ts +3 -1
- package/src/add/add-scenario-step.test.ts +35 -0
- package/src/add/add-scope.test.ts +4 -4
- package/src/add/add-scope.ts +4 -8
- package/src/add/add-secret.ts +1 -0
- package/src/add/add-system-role.test.ts +8 -8
- package/src/add/add-system-role.ts +2 -4
- package/src/add/add-variable.ts +1 -0
- package/src/add/add-wire-remote-addon.ts +4 -1
- package/src/add/add-workflow-graph-foreach.test.ts +174 -0
- package/src/add/add-workflow-graph.ts +78 -6
- package/src/add/add-workflow-sleep-expression.test.ts +3 -4
- package/src/add/addon-contracts.test.ts +1 -1
- package/src/add/config-value-metadata.test.ts +94 -0
- package/src/add/secret-sink-check.test.ts +4 -1
- package/src/error-codes.ts +3 -1
- package/src/index.ts +3 -4
- package/src/inspector.ts +20 -3
- package/src/types.ts +10 -7
- package/src/utils/annotate-http-route-auth.test.ts +5 -7
- package/src/utils/annotate-http-route-auth.ts +2 -1
- package/src/utils/check-secret-sinks.ts +4 -1
- package/src/utils/compute-required-schemas.ts +1 -1
- package/src/utils/contract-hashes.test.ts +1 -1
- package/src/utils/contract-hashes.ts +3 -2
- package/src/utils/extract-function-name.ts +1 -1
- package/src/utils/extract-services.ts +4 -2
- package/src/utils/extract-surface-usage.test.ts +147 -0
- package/src/utils/extract-surface-usage.ts +121 -0
- package/src/utils/filter-inspector-state.test.ts +138 -12
- package/src/utils/filter-inspector-state.ts +82 -21
- package/src/utils/filter-utils.ts +1 -1
- package/src/utils/get-property-value.ts +43 -5
- package/src/utils/load-addon-functions-meta.ts +40 -0
- package/src/utils/middleware.test.ts +23 -23
- package/src/utils/middleware.ts +6 -6
- package/src/utils/permissions.ts +1 -1
- package/src/utils/post-process.test.ts +68 -11
- package/src/utils/post-process.ts +91 -28
- package/src/utils/resolve-deploy-target.ts +1 -1
- package/src/utils/resolve-function-meta.ts +1 -1
- package/src/utils/resolve-function-types.ts +1 -1
- package/src/utils/resolve-versions.ts +1 -1
- package/src/utils/schema-generator.ts +2 -1
- package/src/utils/serialize-inspector-state.ts +10 -7
- package/src/utils/serialize-mcp-json.ts +1 -1
- package/src/utils/serialize-openapi-json.ts +1 -1
- package/src/utils/validate-auth-sessionless.ts +2 -1
- package/src/utils/validate-tags-resolve-to-middleware.test.ts +4 -1
- package/src/utils/workflow/dsl/extract-approval-policy.test.ts +4 -1
- package/src/utils/workflow/graph/convert-dsl-to-graph-skip.test.ts +1 -1
- package/src/utils/workflow/graph/finalize-workflows.ts +6 -2
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +34 -3
- package/src/utils/workflow/graph/workflow-graph.types.ts +10 -0
- package/src/visit.ts +4 -4
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/add/add-ai-scorer.d.ts +0 -2
- package/dist/utils/secret-brokers.d.ts +0 -2
- package/dist/utils/secret-brokers.js +0 -9
|
@@ -128,22 +128,27 @@ export function registerHTTPRoute({ obj, state, checker, logger, sourceFile, bas
|
|
|
128
128
|
const packageName = ts.isIdentifier(funcInitializer)
|
|
129
129
|
? resolveAddonName(funcInitializer, checker, state.rpc.wireAddonDeclarations)
|
|
130
130
|
: null;
|
|
131
|
+
// A ref()-wired route points straight at the addon function, the same way the
|
|
132
|
+
// CLI and channel adders record one. The addon's own metadata is the contract
|
|
133
|
+
// — and it is authoritative where the checker is not: `ref('ns:fn')` only
|
|
134
|
+
// types precisely once the consumer's own rpc map already lists the addon's
|
|
135
|
+
// functions, so a cold run inferred the whole `FlattenedRPCMap` and a warm one
|
|
136
|
+
// inferred the real input. The addon's metadata says which it is either way.
|
|
137
|
+
//
|
|
138
|
+
// The route stays the consuming app's all the same, so it records no
|
|
139
|
+
// packageName: that names the package a wire *runs in*, and running this one
|
|
140
|
+
// inside the addon would hand it the addon's middleware and services instead
|
|
141
|
+
// of the app's. `ref` reaches the addon over RPC, which enters it properly.
|
|
131
142
|
if (refAddonTarget) {
|
|
132
143
|
const targetMeta = resolveFunctionMeta(state, refAddonTarget);
|
|
133
144
|
if (!targetMeta) {
|
|
134
145
|
logger.warn(`Skipping route '${fullRoute}': addon function metadata for '${refAddonTarget}' is not available yet.`);
|
|
135
146
|
return;
|
|
136
147
|
}
|
|
148
|
+
funcName = refAddonTarget;
|
|
137
149
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
// determine whether a session is required for ref()-based routes.
|
|
141
|
-
if (refAddonTarget) {
|
|
142
|
-
const targetMeta = resolveFunctionMeta(state, refAddonTarget);
|
|
143
|
-
const inlineMeta = state.functions.meta[funcName];
|
|
144
|
-
if (targetMeta && inlineMeta && targetMeta.sessionless !== undefined) {
|
|
145
|
-
inlineMeta.sessionless = targetMeta.sessionless;
|
|
146
|
-
}
|
|
150
|
+
else {
|
|
151
|
+
ensureFunctionMetadata(state, funcName, fullRoute, funcInitializer, checker, extracted.isHelper);
|
|
147
152
|
}
|
|
148
153
|
// The route's own `auth`, falling back to the group's. Recorded on the meta
|
|
149
154
|
// below so an audit can answer "is this route open?" without re-reading the
|
|
@@ -175,12 +180,11 @@ export function registerHTTPRoute({ obj, state, checker, logger, sourceFile, bas
|
|
|
175
180
|
}
|
|
176
181
|
const input = fnMeta.inputs?.[0] || null;
|
|
177
182
|
const getRouteInputKeys = () => {
|
|
178
|
-
const
|
|
179
|
-
const inputTypes = state.typesLookup.get(targetFuncName);
|
|
183
|
+
const inputTypes = state.typesLookup.get(funcName);
|
|
180
184
|
if (inputTypes && inputTypes.length > 0) {
|
|
181
185
|
return extractTypeKeys(inputTypes[0]);
|
|
182
186
|
}
|
|
183
|
-
const targetMeta = resolveFunctionMeta(state,
|
|
187
|
+
const targetMeta = resolveFunctionMeta(state, funcName);
|
|
184
188
|
if (targetMeta?.inputSchemaName) {
|
|
185
189
|
const schema = state.schemas[targetMeta.inputSchemaName];
|
|
186
190
|
const properties = schema?.properties;
|
|
@@ -203,7 +207,7 @@ export function registerHTTPRoute({ obj, state, checker, logger, sourceFile, bas
|
|
|
203
207
|
const missingParams = params.filter((p) => !inputKeys.includes(p));
|
|
204
208
|
if (missingParams.length > 0) {
|
|
205
209
|
logger.critical(ErrorCode.ROUTE_PARAM_MISMATCH, `Route '${fullRoute}' has path parameter(s) [${missingParams.join(', ')}] ` +
|
|
206
|
-
`not found in function '${
|
|
210
|
+
`not found in function '${funcName}' input type. ` +
|
|
207
211
|
`Input type has: [${inputKeys.join(', ')}]`);
|
|
208
212
|
return;
|
|
209
213
|
}
|
|
@@ -213,7 +217,7 @@ export function registerHTTPRoute({ obj, state, checker, logger, sourceFile, bas
|
|
|
213
217
|
const missingQuery = query.filter((q) => !inputKeys.includes(q));
|
|
214
218
|
if (missingQuery.length > 0) {
|
|
215
219
|
logger.critical(ErrorCode.ROUTE_QUERY_MISMATCH, `Route '${fullRoute}' has query parameter(s) [${missingQuery.join(', ')}] ` +
|
|
216
|
-
`not found in function '${
|
|
220
|
+
`not found in function '${funcName}' input type. ` +
|
|
217
221
|
`Input type has: [${inputKeys.join(', ')}]`);
|
|
218
222
|
return;
|
|
219
223
|
}
|
|
@@ -227,9 +231,6 @@ export function registerHTTPRoute({ obj, state, checker, logger, sourceFile, bas
|
|
|
227
231
|
// Resolve permissions
|
|
228
232
|
const permissions = resolvePermissions(state, obj, tags, checker);
|
|
229
233
|
state.serviceAggregation.usedFunctions.add(funcName);
|
|
230
|
-
if (refAddonTarget) {
|
|
231
|
-
state.serviceAggregation.usedFunctions.add(refAddonTarget);
|
|
232
|
-
}
|
|
233
234
|
extractWireNames(middleware).forEach((name) => state.serviceAggregation.usedMiddleware.add(name));
|
|
234
235
|
extractWireNames(permissions).forEach((name) => state.serviceAggregation.usedPermissions.add(name));
|
|
235
236
|
// Check for SSE
|
|
@@ -240,7 +241,6 @@ export function registerHTTPRoute({ obj, state, checker, logger, sourceFile, bas
|
|
|
240
241
|
state.http.files.add(sourceFile.fileName);
|
|
241
242
|
state.http.meta[method][fullRoute] = {
|
|
242
243
|
pikkuFuncId: funcName,
|
|
243
|
-
...(refAddonTarget && { refTarget: refAddonTarget }),
|
|
244
244
|
...(packageName && { packageName }),
|
|
245
245
|
route: fullRoute,
|
|
246
246
|
sourceFile: sourceFile.fileName,
|
|
@@ -4,6 +4,8 @@ export interface KeyedWiringConfig {
|
|
|
4
4
|
idField: string;
|
|
5
5
|
label: string;
|
|
6
6
|
schemaPrefix: string;
|
|
7
|
+
/** Boolean property carrying this wiring kind's deploy-gate opt-in/opt-out. */
|
|
8
|
+
flagField: string;
|
|
7
9
|
getState: (state: InspectorState) => {
|
|
8
10
|
definitions: any[];
|
|
9
11
|
files: Set<string>;
|
|
@@ -2,7 +2,7 @@ import * as ts from 'typescript';
|
|
|
2
2
|
import { getPropertyValue, getArrayPropertyValue, assertStringLiteralProperty, } from '../utils/get-property-value.js';
|
|
3
3
|
import { ErrorCode } from '../error-codes.js';
|
|
4
4
|
import { detectSchemaVendorOrError } from '../utils/detect-schema-vendor.js';
|
|
5
|
-
import { parseDurationString } from '@pikku/core';
|
|
5
|
+
import { parseDurationString } from '@pikku/core/utils';
|
|
6
6
|
export const createAddKeyedWiring = (config) => {
|
|
7
7
|
return (logger, node, checker, state, _options) => {
|
|
8
8
|
if (!ts.isCallExpression(node)) {
|
|
@@ -29,6 +29,7 @@ export const createAddKeyedWiring = (config) => {
|
|
|
29
29
|
const docsUrlValue = getPropertyValue(obj, 'docsUrl');
|
|
30
30
|
const allowedHostsValue = getArrayPropertyValue(obj, 'allowedHosts');
|
|
31
31
|
const idValue = getPropertyValue(obj, config.idField);
|
|
32
|
+
const flagValue = getPropertyValue(obj, config.flagField) === true;
|
|
32
33
|
let schemaVariableName = null;
|
|
33
34
|
let schemaSourceFile = null;
|
|
34
35
|
let schemaIdentifier = null;
|
|
@@ -107,6 +108,7 @@ export const createAddKeyedWiring = (config) => {
|
|
|
107
108
|
docsUrl: docsUrlValue || undefined,
|
|
108
109
|
allowedHosts: allowedHostsValue || undefined,
|
|
109
110
|
[config.idField]: idValue,
|
|
111
|
+
[config.flagField]: flagValue || undefined,
|
|
110
112
|
schema: schemaLookupName,
|
|
111
113
|
sourceFile,
|
|
112
114
|
});
|
|
@@ -508,12 +508,12 @@ export const addMiddleware = (logger, node, checker, state) => {
|
|
|
508
508
|
logger.debug(`• Found channel middleware factory with services: ${services.services.join(', ')}`);
|
|
509
509
|
return;
|
|
510
510
|
}
|
|
511
|
-
if (expression.text === '
|
|
511
|
+
if (expression.text === 'pikkuAgentMiddleware') {
|
|
512
512
|
const arg = args[0];
|
|
513
513
|
if (!arg)
|
|
514
514
|
return;
|
|
515
515
|
if (!ts.isObjectLiteralExpression(arg)) {
|
|
516
|
-
logger.error(`•
|
|
516
|
+
logger.error(`• pikkuAgentMiddleware() requires an object literal argument.`);
|
|
517
517
|
return;
|
|
518
518
|
}
|
|
519
519
|
const allServices = new Set();
|
|
@@ -542,12 +542,12 @@ export const addMiddleware = (logger, node, checker, state) => {
|
|
|
542
542
|
pikkuFuncId = node.parent.name.text;
|
|
543
543
|
}
|
|
544
544
|
else {
|
|
545
|
-
logger.error(`•
|
|
546
|
-
`Extract it to a const: const myMiddleware =
|
|
545
|
+
logger.error(`• pikkuAgentMiddleware() must be assigned to a variable or object property. ` +
|
|
546
|
+
`Extract it to a const: const myMiddleware = pikkuAgentMiddleware(...)`);
|
|
547
547
|
return;
|
|
548
548
|
}
|
|
549
549
|
}
|
|
550
|
-
state.
|
|
550
|
+
state.agentMiddleware.definitions[pikkuFuncId] = {
|
|
551
551
|
services,
|
|
552
552
|
sourceFile: node.getSourceFile().fileName,
|
|
553
553
|
position: node.getStart(),
|
package/dist/add/add-personas.js
CHANGED
|
@@ -192,6 +192,9 @@ export const addPersonas = (logger, node, _checker, state) => {
|
|
|
192
192
|
const personality = stringProperty(config, 'personality');
|
|
193
193
|
if (personality !== undefined)
|
|
194
194
|
persona.personality = personality;
|
|
195
|
+
const app = stringProperty(config, 'app');
|
|
196
|
+
if (app !== undefined)
|
|
197
|
+
persona.app = app;
|
|
195
198
|
if (disposition !== undefined) {
|
|
196
199
|
persona.disposition = disposition;
|
|
197
200
|
}
|
package/dist/add/add-secret.js
CHANGED
package/dist/add/add-variable.js
CHANGED
|
@@ -38,7 +38,8 @@ export function addWireRemoteAddon(node, state, logger) {
|
|
|
38
38
|
ts.isObjectLiteralExpression(prop.initializer)) {
|
|
39
39
|
hasAuth = true;
|
|
40
40
|
for (const authProp of prop.initializer.properties) {
|
|
41
|
-
if (!ts.isPropertyAssignment(authProp) ||
|
|
41
|
+
if (!ts.isPropertyAssignment(authProp) ||
|
|
42
|
+
!ts.isIdentifier(authProp.name))
|
|
42
43
|
continue;
|
|
43
44
|
if (authProp.name.text === 'credentialId' &&
|
|
44
45
|
ts.isStringLiteral(authProp.initializer)) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
2
|
import { ErrorCode } from '../error-codes.js';
|
|
3
3
|
import { extractStringLiteral } from '../utils/extract-node-value.js';
|
|
4
|
-
function extractAstValue(expr, refParamName, templateParamName) {
|
|
4
|
+
function extractAstValue(expr, refParamName, templateParamName, itemParamName) {
|
|
5
5
|
// `'GET' as const` / parenthesised values wrap the real initializer — see
|
|
6
6
|
// through them or the whole property is silently dropped from the meta.
|
|
7
7
|
while (ts.isAsExpression(expr) || ts.isParenthesizedExpression(expr)) {
|
|
@@ -31,6 +31,11 @@ function extractAstValue(expr, refParamName, templateParamName) {
|
|
|
31
31
|
const path = args[1] && ts.isStringLiteral(args[1]) ? args[1].text : undefined;
|
|
32
32
|
return { $ref: nodeId, path };
|
|
33
33
|
}
|
|
34
|
+
if (itemParamName && callee.text === itemParamName) {
|
|
35
|
+
const pathArg = expr.arguments[0];
|
|
36
|
+
const path = pathArg && ts.isStringLiteral(pathArg) ? pathArg.text : undefined;
|
|
37
|
+
return { $ref: '$item', path };
|
|
38
|
+
}
|
|
34
39
|
if (templateParamName && callee.text === templateParamName) {
|
|
35
40
|
const templateStr = expr.arguments[0] && ts.isStringLiteral(expr.arguments[0])
|
|
36
41
|
? expr.arguments[0].text
|
|
@@ -39,7 +44,7 @@ function extractAstValue(expr, refParamName, templateParamName) {
|
|
|
39
44
|
const refs = [];
|
|
40
45
|
if (refsArg && ts.isArrayLiteralExpression(refsArg)) {
|
|
41
46
|
for (const el of refsArg.elements) {
|
|
42
|
-
const resolved = extractAstValue(el, refParamName, templateParamName);
|
|
47
|
+
const resolved = extractAstValue(el, refParamName, templateParamName, itemParamName);
|
|
43
48
|
if (typeof resolved === 'object' &&
|
|
44
49
|
resolved !== null &&
|
|
45
50
|
'$ref' in resolved) {
|
|
@@ -64,7 +69,7 @@ function extractAstValue(expr, refParamName, templateParamName) {
|
|
|
64
69
|
}
|
|
65
70
|
}
|
|
66
71
|
if (ts.isArrayLiteralExpression(expr)) {
|
|
67
|
-
return expr.elements.map((el) => extractAstValue(el, refParamName, templateParamName));
|
|
72
|
+
return expr.elements.map((el) => extractAstValue(el, refParamName, templateParamName, itemParamName));
|
|
68
73
|
}
|
|
69
74
|
if (ts.isObjectLiteralExpression(expr)) {
|
|
70
75
|
const obj = {};
|
|
@@ -78,7 +83,7 @@ function extractAstValue(expr, refParamName, templateParamName) {
|
|
|
78
83
|
: null;
|
|
79
84
|
if (!key)
|
|
80
85
|
continue;
|
|
81
|
-
obj[key] = extractAstValue(prop.initializer, refParamName, templateParamName);
|
|
86
|
+
obj[key] = extractAstValue(prop.initializer, refParamName, templateParamName, itemParamName);
|
|
82
87
|
}
|
|
83
88
|
return obj;
|
|
84
89
|
}
|
|
@@ -125,6 +130,11 @@ function extractInputMapping(node, _checker) {
|
|
|
125
130
|
const templateParamName = node.parameters.length > 1 && ts.isIdentifier(node.parameters[1].name)
|
|
126
131
|
? node.parameters[1].name.text
|
|
127
132
|
: 'template';
|
|
133
|
+
// `$item` is appended after `template` so neither existing arrow shape —
|
|
134
|
+
// `(ref) => ...` nor `(ref, template) => ...` — shifts a parameter.
|
|
135
|
+
const itemParamName = node.parameters.length > 2 && ts.isIdentifier(node.parameters[2].name)
|
|
136
|
+
? node.parameters[2].name.text
|
|
137
|
+
: undefined;
|
|
128
138
|
const input = {};
|
|
129
139
|
for (const prop of bodyObj.properties) {
|
|
130
140
|
if (!ts.isPropertyAssignment(prop))
|
|
@@ -136,13 +146,48 @@ function extractInputMapping(node, _checker) {
|
|
|
136
146
|
: null;
|
|
137
147
|
if (!key)
|
|
138
148
|
continue;
|
|
139
|
-
const value = extractAstValue(prop.initializer, refParamName, templateParamName);
|
|
149
|
+
const value = extractAstValue(prop.initializer, refParamName, templateParamName, itemParamName);
|
|
140
150
|
if (value !== undefined) {
|
|
141
151
|
input[key] = value;
|
|
142
152
|
}
|
|
143
153
|
}
|
|
144
154
|
return input;
|
|
145
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Extract a `forEach` source, authored either as a bare node id (`'listRows'`)
|
|
158
|
+
* or as a callback returning a ref (`(ref) => ref('listRows', 'rows')`).
|
|
159
|
+
*/
|
|
160
|
+
function extractForEachConfig(node) {
|
|
161
|
+
while (ts.isAsExpression(node) || ts.isParenthesizedExpression(node)) {
|
|
162
|
+
node = node.expression;
|
|
163
|
+
}
|
|
164
|
+
if (ts.isStringLiteral(node)) {
|
|
165
|
+
return { $ref: node.text };
|
|
166
|
+
}
|
|
167
|
+
if (ts.isArrowFunction(node)) {
|
|
168
|
+
const refParamName = node.parameters.length > 0 && ts.isIdentifier(node.parameters[0].name)
|
|
169
|
+
? node.parameters[0].name.text
|
|
170
|
+
: 'ref';
|
|
171
|
+
let body = node.body;
|
|
172
|
+
if (ts.isBlock(body)) {
|
|
173
|
+
for (const stmt of body.statements) {
|
|
174
|
+
if (ts.isReturnStatement(stmt) && stmt.expression) {
|
|
175
|
+
body = stmt.expression;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (!ts.isExpression(body))
|
|
180
|
+
return undefined;
|
|
181
|
+
const value = extractAstValue(body, refParamName, undefined);
|
|
182
|
+
if (typeof value === 'object' &&
|
|
183
|
+
value !== null &&
|
|
184
|
+
'$ref' in value &&
|
|
185
|
+
typeof value.$ref === 'string') {
|
|
186
|
+
return value;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
146
191
|
/**
|
|
147
192
|
* Extract next config (string, array, or record)
|
|
148
193
|
*/
|
|
@@ -301,6 +346,8 @@ function extractGraphFromNewFormat(nodesNode, configNode, checker, state) {
|
|
|
301
346
|
nodeId,
|
|
302
347
|
rpcName,
|
|
303
348
|
input: {},
|
|
349
|
+
forEach: undefined,
|
|
350
|
+
mode: undefined,
|
|
304
351
|
next: undefined,
|
|
305
352
|
onError: undefined,
|
|
306
353
|
retries: undefined,
|
|
@@ -326,6 +373,8 @@ function extractGraphFromNewFormat(nodesNode, configNode, checker, state) {
|
|
|
326
373
|
nodes[nodeId].next = nodeConfig.next;
|
|
327
374
|
nodes[nodeId].onError = nodeConfig.onError;
|
|
328
375
|
nodes[nodeId].input = nodeConfig.input;
|
|
376
|
+
nodes[nodeId].forEach = nodeConfig.forEach;
|
|
377
|
+
nodes[nodeId].mode = nodeConfig.mode;
|
|
329
378
|
nodes[nodeId].retries = nodeConfig.retries;
|
|
330
379
|
nodes[nodeId].retryDelay = nodeConfig.retryDelay;
|
|
331
380
|
nodes[nodeId].notes = nodeConfig.notes;
|
|
@@ -342,6 +391,8 @@ function extractNodeConfigFromObject(obj, checker) {
|
|
|
342
391
|
let next = undefined;
|
|
343
392
|
let onError = undefined;
|
|
344
393
|
let input = {};
|
|
394
|
+
let forEach = undefined;
|
|
395
|
+
let mode = undefined;
|
|
345
396
|
let retries = undefined;
|
|
346
397
|
let retryDelay = undefined;
|
|
347
398
|
let notes = undefined;
|
|
@@ -358,6 +409,12 @@ function extractNodeConfigFromObject(obj, checker) {
|
|
|
358
409
|
else if (propName === 'input') {
|
|
359
410
|
input = extractInputMapping(prop.initializer, checker);
|
|
360
411
|
}
|
|
412
|
+
else if (propName === 'forEach') {
|
|
413
|
+
forEach = extractForEachConfig(prop.initializer);
|
|
414
|
+
}
|
|
415
|
+
else if (propName === 'mode') {
|
|
416
|
+
mode = extractStringLiteral(prop.initializer, checker);
|
|
417
|
+
}
|
|
361
418
|
else if (propName === 'retries') {
|
|
362
419
|
if (ts.isNumericLiteral(prop.initializer)) {
|
|
363
420
|
retries = Number(prop.initializer.text);
|
|
@@ -375,7 +432,7 @@ function extractNodeConfigFromObject(obj, checker) {
|
|
|
375
432
|
notes = extractStringLiteral(prop.initializer, checker);
|
|
376
433
|
}
|
|
377
434
|
}
|
|
378
|
-
return { next, onError, input, retries, retryDelay, notes };
|
|
435
|
+
return { next, onError, input, forEach, mode, retries, retryDelay, notes };
|
|
379
436
|
}
|
|
380
437
|
/**
|
|
381
438
|
* Inspector for pikkuWorkflowGraph() calls
|
package/dist/error-codes.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export declare enum ErrorCode {
|
|
|
20
20
|
CLI_CLIENTSIDE_RENDERER_HAS_SERVICES = "PKU672",
|
|
21
21
|
SCENARIO_HAS_SERVICES = "PKU673",
|
|
22
22
|
EXPECT_EVENTUALLY_SCENARIO_ONLY = "PKU675",
|
|
23
|
-
|
|
23
|
+
SCENARIO_STEP_NEEDS_ACTOR = "PKU677",
|
|
24
24
|
SCENARIO_STEP_TARGET_NOT_STATIC = "PKU678",
|
|
25
25
|
SCENARIO_NOT_EXTRACTABLE = "PKU679",
|
|
26
26
|
SCENARIO_HAS_NO_ASSERTION = "PKU680",
|
|
@@ -30,6 +30,7 @@ export declare enum ErrorCode {
|
|
|
30
30
|
COMPLEX_WORKFLOW_NOT_ALLOWED = "PKU643",
|
|
31
31
|
REMOTE_ADDON_NOT_DEV_DEPENDENCY = "PKU338",
|
|
32
32
|
REMOTE_ADDON_AUTH_UNRESOLVED = "PKU339",
|
|
33
|
+
ADDON_NOT_INSTALLED = "PKU340",
|
|
33
34
|
DB_COLUMN_NAME_TYPE_CONTRADICTION = "PKU480",
|
|
34
35
|
DB_JSON_COLUMN_UNTYPED = "PKU481",
|
|
35
36
|
DB_FORMAT_ON_NON_STRING = "PKU482",
|
|
@@ -70,6 +71,7 @@ export declare enum ErrorCode {
|
|
|
70
71
|
DUPLICATE_CORE_VERSION = "PKU717",
|
|
71
72
|
CORE_VERSION_SKEW = "PKU718",
|
|
72
73
|
SPLIT_TYPE_IDENTITY = "PKU719",
|
|
74
|
+
SINGLETON_SERVICES_TYPE_UNRESOLVED = "PKU724",
|
|
73
75
|
PII_IN_OUTPUT = "PKU910",
|
|
74
76
|
ADDON_WIRING_NOT_ALLOWED = "PKU920",
|
|
75
77
|
ADDON_CONTRACT_HANDLERS_NOT_ALLOWED = "PKU921",
|
package/dist/error-codes.js
CHANGED
|
@@ -22,7 +22,7 @@ export var ErrorCode;
|
|
|
22
22
|
ErrorCode["CLI_CLIENTSIDE_RENDERER_HAS_SERVICES"] = "PKU672";
|
|
23
23
|
ErrorCode["SCENARIO_HAS_SERVICES"] = "PKU673";
|
|
24
24
|
ErrorCode["EXPECT_EVENTUALLY_SCENARIO_ONLY"] = "PKU675";
|
|
25
|
-
ErrorCode["
|
|
25
|
+
ErrorCode["SCENARIO_STEP_NEEDS_ACTOR"] = "PKU677";
|
|
26
26
|
ErrorCode["SCENARIO_STEP_TARGET_NOT_STATIC"] = "PKU678";
|
|
27
27
|
ErrorCode["SCENARIO_NOT_EXTRACTABLE"] = "PKU679";
|
|
28
28
|
ErrorCode["SCENARIO_HAS_NO_ASSERTION"] = "PKU680";
|
|
@@ -33,6 +33,7 @@ export var ErrorCode;
|
|
|
33
33
|
// Remote addon (wireRemoteAddon) validation
|
|
34
34
|
ErrorCode["REMOTE_ADDON_NOT_DEV_DEPENDENCY"] = "PKU338";
|
|
35
35
|
ErrorCode["REMOTE_ADDON_AUTH_UNRESOLVED"] = "PKU339";
|
|
36
|
+
ErrorCode["ADDON_NOT_INSTALLED"] = "PKU340";
|
|
36
37
|
// Database schema codegen warnings
|
|
37
38
|
ErrorCode["DB_COLUMN_NAME_TYPE_CONTRADICTION"] = "PKU480";
|
|
38
39
|
ErrorCode["DB_JSON_COLUMN_UNTYPED"] = "PKU481";
|
|
@@ -91,6 +92,7 @@ export var ErrorCode;
|
|
|
91
92
|
ErrorCode["DUPLICATE_CORE_VERSION"] = "PKU717";
|
|
92
93
|
ErrorCode["CORE_VERSION_SKEW"] = "PKU718";
|
|
93
94
|
ErrorCode["SPLIT_TYPE_IDENTITY"] = "PKU719";
|
|
95
|
+
ErrorCode["SINGLETON_SERVICES_TYPE_UNRESOLVED"] = "PKU724";
|
|
94
96
|
// Data classification errors
|
|
95
97
|
ErrorCode["PII_IN_OUTPUT"] = "PKU910";
|
|
96
98
|
// Addon authoring errors
|
package/dist/index.d.ts
CHANGED
|
@@ -12,9 +12,11 @@ export type { SerializableInspectorState } from './utils/serialize-inspector-sta
|
|
|
12
12
|
export { filterInspectorState } from './utils/filter-inspector-state.js';
|
|
13
13
|
export { resolveDeployTarget } from './utils/resolve-deploy-target.js';
|
|
14
14
|
export { generateCustomTypes, sanitizeTypeName, } from './utils/custom-types-generator.js';
|
|
15
|
+
export { unresolvedSchemaReferences } from './utils/post-process.js';
|
|
16
|
+
export type { SurfaceUsageCounts } from './utils/extract-surface-usage.js';
|
|
15
17
|
export { createEmptyManifest, serializeManifest, } from './utils/contract-hashes.js';
|
|
16
18
|
export type { ContractEntry, VersionHashEntry, VersionValidateError, VersionManifest, VersionManifestEntry, } from './utils/contract-hashes.js';
|
|
17
|
-
export type { InspectorFeature, InspectorFeatureEntry
|
|
19
|
+
export type { InspectorFeature, InspectorFeatureEntry } from './types.js';
|
|
18
20
|
export { serializeMCPJson } from './utils/serialize-mcp-json.js';
|
|
19
21
|
export type { OpenAPISpecInfo } from './utils/serialize-openapi-json.js';
|
|
20
22
|
export { deserializeDslWorkflow, deserializeGraphWorkflow, deserializeAllDslWorkflows, } from './utils/workflow/dsl/index.js';
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export { serializeInspectorState, deserializeInspectorState, } from './utils/ser
|
|
|
6
6
|
export { filterInspectorState } from './utils/filter-inspector-state.js';
|
|
7
7
|
export { resolveDeployTarget } from './utils/resolve-deploy-target.js';
|
|
8
8
|
export { generateCustomTypes, sanitizeTypeName, } from './utils/custom-types-generator.js';
|
|
9
|
+
export { unresolvedSchemaReferences } from './utils/post-process.js';
|
|
9
10
|
export { createEmptyManifest, serializeManifest, } from './utils/contract-hashes.js';
|
|
10
11
|
export { serializeMCPJson } from './utils/serialize-mcp-json.js';
|
|
11
12
|
export { deserializeDslWorkflow, deserializeGraphWorkflow, deserializeAllDslWorkflows, } from './utils/workflow/dsl/index.js';
|
package/dist/inspector.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
2
|
import { performance } from 'perf_hooks';
|
|
3
|
-
import { dirname, resolve } from 'path';
|
|
3
|
+
import { dirname, relative, resolve } from 'path';
|
|
4
4
|
import { visitSetup, visitFunctions, visitRoutes } from './visit.js';
|
|
5
5
|
import { TypesMap } from './types-map.js';
|
|
6
6
|
import { getFilesAndMethods } from './utils/get-files-and-methods.js';
|
|
@@ -11,12 +11,13 @@ import { validateExposedFunctionsGated } from './utils/validate-exposed-function
|
|
|
11
11
|
import { validateTagsResolveToMiddleware } from './utils/validate-tags-resolve-to-middleware.js';
|
|
12
12
|
import { annotateHttpRouteAuth } from './utils/annotate-http-route-auth.js';
|
|
13
13
|
import { generateOpenAPISpec } from './utils/serialize-openapi-json.js';
|
|
14
|
-
import { pikkuState } from '@pikku/core/
|
|
14
|
+
import { pikkuState } from '@pikku/core/state';
|
|
15
15
|
import { resolveLatestVersions } from './utils/resolve-versions.js';
|
|
16
16
|
import { finalizeWorkflows } from './utils/workflow/graph/finalize-workflows.js';
|
|
17
17
|
import { finalizeWorkflowHelperTypes, finalizeWorkflowWires, } from './utils/workflow/graph/finalize-workflow-wires.js';
|
|
18
18
|
import { generateAllSchemas } from './utils/schema-generator.js';
|
|
19
19
|
import { extractSecretUsage } from './utils/extract-secret-usage.js';
|
|
20
|
+
import { accumulateSurfaceUsage, surfaceUsageArea, } from './utils/extract-surface-usage.js';
|
|
20
21
|
import { loadAddonFunctionsMeta, loadAddonSchemas, } from './utils/load-addon-functions-meta.js';
|
|
21
22
|
import { computeContractHashes, extractContractsFromMeta, updateManifest, createEmptyManifest, validateContracts, } from './utils/contract-hashes.js';
|
|
22
23
|
/**
|
|
@@ -142,6 +143,7 @@ export function getInitialInspectorState(rootDir) {
|
|
|
142
143
|
files: new Set(),
|
|
143
144
|
usage: new Map(),
|
|
144
145
|
},
|
|
146
|
+
surfaceUsage: {},
|
|
145
147
|
credentials: {
|
|
146
148
|
definitions: [],
|
|
147
149
|
files: new Set(),
|
|
@@ -177,7 +179,7 @@ export function getInitialInspectorState(rootDir) {
|
|
|
177
179
|
instances: {},
|
|
178
180
|
tagMiddleware: new Map(),
|
|
179
181
|
},
|
|
180
|
-
|
|
182
|
+
agentMiddleware: {
|
|
181
183
|
definitions: {},
|
|
182
184
|
},
|
|
183
185
|
permissions: {
|
|
@@ -299,11 +301,19 @@ export const inspect = async (logger, routeFiles, options = {}) => {
|
|
|
299
301
|
.sort((a, b) => a.fileName < b.fileName ? -1 : a.fileName > b.fileName ? 1 : 0);
|
|
300
302
|
logger.debug(`Got source files in ${(performance.now() - startSourceFiles).toFixed(2)}ms`);
|
|
301
303
|
const state = getInitialInspectorState(rootDir);
|
|
304
|
+
const surfaceAreaCache = new Map();
|
|
302
305
|
for (const sourceFile of sourceFiles) {
|
|
303
306
|
const usage = extractSecretUsage(sourceFile);
|
|
304
307
|
if (usage.keys.length > 0 || usage.dynamic.length > 0) {
|
|
305
308
|
state.secrets.usage.set(sourceFile.fileName, usage);
|
|
306
309
|
}
|
|
310
|
+
// Counted in the sweep that already has this file open. The public surface
|
|
311
|
+
// is documentation, and documentation may not cost a project a second pass
|
|
312
|
+
// over its source.
|
|
313
|
+
accumulateSurfaceUsage(sourceFile, {
|
|
314
|
+
area: surfaceUsageArea(sourceFile.fileName, rootDir, surfaceAreaCache),
|
|
315
|
+
file: relative(rootDir, sourceFile.fileName),
|
|
316
|
+
}, state.surfaceUsage);
|
|
307
317
|
}
|
|
308
318
|
// First sweep: add all functions
|
|
309
319
|
const startSetup = performance.now();
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type * as ts from 'typescript';
|
|
2
2
|
import type { SecretUsage } from './utils/extract-secret-usage.js';
|
|
3
|
+
import type { SurfaceUsageCounts } from './utils/extract-surface-usage.js';
|
|
3
4
|
import type { ChannelMessageMeta, ChannelsMeta } from '@pikku/core/channel';
|
|
4
5
|
import type { GatewaysMeta } from '@pikku/core/gateway';
|
|
5
6
|
import type { HTTPWiringsMeta } from '@pikku/core/http';
|
|
@@ -8,8 +9,8 @@ import type { TriggerMeta, TriggerSourceMeta } from '@pikku/core/trigger';
|
|
|
8
9
|
import type { QueueWorkersMeta } from '@pikku/core/queue';
|
|
9
10
|
import type { WorkflowsMeta } from '@pikku/core/workflow';
|
|
10
11
|
import type { MCPResourceMeta, MCPToolMeta, MCPPromptMeta } from '@pikku/core/mcp';
|
|
11
|
-
import type {
|
|
12
|
-
import type { ScorerMeta } from '@pikku/core/
|
|
12
|
+
import type { AgentsMeta } from '@pikku/core/agent';
|
|
13
|
+
import type { ScorerMeta } from '@pikku/core/agent-scorer';
|
|
13
14
|
import type { CLIMeta } from '@pikku/core/cli';
|
|
14
15
|
import type { CLICommandMeta } from '@pikku/core/cli';
|
|
15
16
|
import type { NodesMeta } from '@pikku/core/node';
|
|
@@ -20,7 +21,9 @@ import type { SystemRoleDefinitions } from '@pikku/core/role';
|
|
|
20
21
|
import type { PersonaDefinitions } from '@pikku/core/persona';
|
|
21
22
|
import type { VariableDefinitions } from '@pikku/core/variable';
|
|
22
23
|
import type { TypesMap } from './types-map.js';
|
|
23
|
-
import type { FunctionsMeta
|
|
24
|
+
import type { FunctionsMeta } from '@pikku/core/services';
|
|
25
|
+
import type { FunctionServicesMeta, FunctionWiresMeta } from '@pikku/core/function';
|
|
26
|
+
import type { JSONValue } from '@pikku/core/utils';
|
|
24
27
|
import type { OpenAPISpecInfo } from './utils/serialize-openapi-json.js';
|
|
25
28
|
import type { ErrorCode, CodedDiagnostic } from './error-codes.js';
|
|
26
29
|
import type { VersionManifest, VersionValidateError } from './utils/contract-hashes.js';
|
|
@@ -545,7 +548,7 @@ export interface InspectorState {
|
|
|
545
548
|
files: Set<string>;
|
|
546
549
|
};
|
|
547
550
|
agents: {
|
|
548
|
-
agentsMeta:
|
|
551
|
+
agentsMeta: AgentsMeta;
|
|
549
552
|
files: Map<string, {
|
|
550
553
|
path: string;
|
|
551
554
|
exportedName: string;
|
|
@@ -592,6 +595,8 @@ export interface InspectorState {
|
|
|
592
595
|
/** Secret reads found per source file, keyed by absolute path. */
|
|
593
596
|
usage: Map<string, SecretUsage>;
|
|
594
597
|
};
|
|
598
|
+
/** How often each `#pikku/*` export is imported, and from where. */
|
|
599
|
+
surfaceUsage: SurfaceUsageCounts;
|
|
595
600
|
credentials: {
|
|
596
601
|
definitions: CredentialDefinitions;
|
|
597
602
|
files: Set<string>;
|
|
@@ -619,7 +624,7 @@ export interface InspectorState {
|
|
|
619
624
|
};
|
|
620
625
|
middleware: InspectorMiddlewareState;
|
|
621
626
|
channelMiddleware: InspectorChannelMiddlewareState;
|
|
622
|
-
|
|
627
|
+
agentMiddleware: InspectorAIMiddlewareState;
|
|
623
628
|
permissions: InspectorPermissionState;
|
|
624
629
|
serviceAggregation: {
|
|
625
630
|
requiredServices: Set<string>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveFunctionMeta } from './resolve-function-meta.js';
|
|
1
2
|
/**
|
|
2
3
|
* Resolve, per HTTP route, whether reaching it requires a session.
|
|
3
4
|
*
|
|
@@ -22,7 +23,7 @@ export function annotateHttpRouteAuth(state) {
|
|
|
22
23
|
const gatedPackages = packagesGatedByTheirAddon(state);
|
|
23
24
|
for (const routes of Object.values(state.http?.meta ?? {})) {
|
|
24
25
|
for (const route of Object.values(routes)) {
|
|
25
|
-
const meta = state
|
|
26
|
+
const meta = resolveFunctionMeta(state, route.pikkuFuncId);
|
|
26
27
|
route.requiresSession =
|
|
27
28
|
// A pikkuFunc always requires a session, on every path.
|
|
28
29
|
meta?.sessionless === false ||
|
|
@@ -38,7 +38,8 @@ const SINKS = [
|
|
|
38
38
|
export function findRevealedSecretSinks(checker, handler) {
|
|
39
39
|
const violations = [];
|
|
40
40
|
const visit = (node) => {
|
|
41
|
-
if (ts.isCallExpression(node) &&
|
|
41
|
+
if (ts.isCallExpression(node) &&
|
|
42
|
+
ts.isPropertyAccessExpression(node.expression)) {
|
|
42
43
|
const sink = describeSink(checker, node.expression);
|
|
43
44
|
if (sink) {
|
|
44
45
|
for (const arg of node.arguments) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FunctionsMeta } from '@pikku/core';
|
|
1
|
+
import type { FunctionsMeta } from '@pikku/core/services';
|
|
2
2
|
import type { TypesMap } from '../types-map.js';
|
|
3
3
|
import type { SchemaRef } from '../types.js';
|
|
4
4
|
export declare function computeRequiredSchemas(functionsMeta: FunctionsMeta, typesMap: TypesMap, additionalTypes?: string[], schemaLookup?: Map<string, SchemaRef>): Set<string>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { FunctionsMeta
|
|
1
|
+
import type { FunctionsMeta } from '@pikku/core/services';
|
|
2
|
+
import type { JSONValue } from '@pikku/core/utils';
|
|
2
3
|
import type { TypesMap } from '../types-map.js';
|
|
3
4
|
import { ErrorCode } from '../error-codes.js';
|
|
4
5
|
export type ContractEntry = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
2
|
import { createHash } from 'crypto';
|
|
3
3
|
import { relative } from 'path';
|
|
4
|
-
import { formatVersionedId } from '@pikku/core';
|
|
4
|
+
import { formatVersionedId } from '@pikku/core/utils';
|
|
5
5
|
/**
|
|
6
6
|
* Deterministic placeholder id for an anonymous/unnamed pikku function or
|
|
7
7
|
* permission. Derived from the call expression's source location (relative path
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
-
import type { FunctionServicesMeta, FunctionWiresMeta } from '@pikku/core';
|
|
2
|
+
import type { FunctionServicesMeta, FunctionWiresMeta } from '@pikku/core/function';
|
|
3
3
|
/**
|
|
4
4
|
* Extract services from a function's first parameter destructuring pattern
|
|
5
5
|
*/
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* How often each `#pikku/*` export is imported, the source areas it was
|
|
4
|
+
* imported from, and the files those imports are written in. Counters only:
|
|
5
|
+
* this is accumulated during the sweep that already visits every source file,
|
|
6
|
+
* so nothing here may retain a node.
|
|
7
|
+
*/
|
|
8
|
+
export type SurfaceUsageCounts = Record<string, Record<string, {
|
|
9
|
+
imports: number;
|
|
10
|
+
seenIn: string[];
|
|
11
|
+
files: string[];
|
|
12
|
+
}>>;
|
|
13
|
+
/**
|
|
14
|
+
* A label a person recognises: the workspace package a file belongs to, or the
|
|
15
|
+
* top-level source directory it sits under when the project is one package.
|
|
16
|
+
* The root package is skipped deliberately — naming it would label every file
|
|
17
|
+
* in a single-package project identically.
|
|
18
|
+
*/
|
|
19
|
+
export declare const surfaceUsageArea: (fileName: string, rootDir: string, cache: Map<string, string | null>) => string;
|
|
20
|
+
/**
|
|
21
|
+
* Counts the `#pikku/*` imports of one file into `into`. Only the file's own
|
|
22
|
+
* top-level statements are looked at — an import declaration can appear nowhere
|
|
23
|
+
* else — so this costs a single pass over the statement list and retains
|
|
24
|
+
* nothing.
|
|
25
|
+
*/
|
|
26
|
+
export declare const accumulateSurfaceUsage: (sourceFile: ts.SourceFile, { area, file }: {
|
|
27
|
+
area: string;
|
|
28
|
+
file: string;
|
|
29
|
+
}, into: SurfaceUsageCounts) => void;
|