@pikku/inspector 0.12.110 → 0.12.112
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/dist/add/add-channel.js +8 -0
- package/dist/add/add-define-contributors.d.ts +10 -0
- package/dist/add/add-define-contributors.js +98 -0
- package/dist/add/add-file-with-factory.d.ts +7 -1
- package/dist/add/add-file-with-factory.js +63 -2
- package/dist/add/add-functions.js +18 -1
- package/dist/add/add-pikku-studio.d.ts +15 -0
- package/dist/add/add-pikku-studio.js +307 -0
- package/dist/add/add-rpc-invocations.js +12 -0
- package/dist/add/add-wire-addon.js +11 -2
- package/dist/add/check-disabled-references.d.ts +27 -0
- package/dist/add/check-disabled-references.js +226 -0
- package/dist/error-codes.d.ts +14 -0
- package/dist/error-codes.js +14 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/initial-state.js +1 -0
- package/dist/inspector.js +4 -0
- package/dist/lite.d.ts +3 -0
- package/dist/lite.js +2 -0
- package/dist/types.d.ts +78 -28
- package/dist/utils/addon-dependencies.d.ts +43 -0
- package/dist/utils/addon-dependencies.js +91 -0
- package/dist/utils/find-unused-functions.d.ts +20 -0
- package/dist/utils/find-unused-functions.js +132 -0
- package/dist/utils/get-property-value.js +4 -1
- package/dist/utils/load-addon-functions-meta.d.ts +1 -8
- package/dist/utils/load-addon-functions-meta.js +87 -76
- package/dist/utils/post-process.js +31 -7
- package/dist/utils/serialize-inspector-state.d.ts +6 -3
- package/dist/utils/serialize-inspector-state.js +12 -6
- package/dist/visit.js +8 -3
- package/package.json +3 -3
- package/dist/add/add-define-screens.d.ts +0 -8
- package/dist/add/add-define-screens.js +0 -140
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
import { relative } from 'node:path';
|
|
3
|
+
import { ErrorCode } from '../error-codes.js';
|
|
4
|
+
/**
|
|
5
|
+
* A function's `disabled`, read from its config: `true`, or a non-empty string
|
|
6
|
+
* saying why. Anything else the build cannot read statically is refused rather
|
|
7
|
+
* than taken as enabled.
|
|
8
|
+
*/
|
|
9
|
+
export const readFunctionDisabled = (obj, name, logger) => {
|
|
10
|
+
for (const prop of obj.properties) {
|
|
11
|
+
if (!ts.isPropertyAssignment(prop) ||
|
|
12
|
+
!ts.isIdentifier(prop.name) ||
|
|
13
|
+
prop.name.text !== 'disabled') {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
const init = prop.initializer;
|
|
17
|
+
if (init.kind === ts.SyntaxKind.TrueKeyword)
|
|
18
|
+
return {};
|
|
19
|
+
if (init.kind === ts.SyntaxKind.FalseKeyword)
|
|
20
|
+
return null;
|
|
21
|
+
if ((ts.isStringLiteral(init) || ts.isNoSubstitutionTemplateLiteral(init)) &&
|
|
22
|
+
init.text !== '') {
|
|
23
|
+
return { reason: init.text };
|
|
24
|
+
}
|
|
25
|
+
logger.critical(ErrorCode.INVALID_VALUE, `'${name}' has a \`disabled\` the build cannot read. Use \`true\` or a non-empty string literal.`);
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
};
|
|
30
|
+
const describe = (state, meta) => `'${meta.name}' is disabled${meta.reason ? ` (${meta.reason})` : ''} in ${relative(state.rootDir, meta.sourceFile)}`;
|
|
31
|
+
const position = (state, node) => {
|
|
32
|
+
const sourceFile = node.getSourceFile();
|
|
33
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
34
|
+
return `${relative(state.rootDir, sourceFile.fileName)}:${line + 1}`;
|
|
35
|
+
};
|
|
36
|
+
/** The call a reference sits in, by its callee: `wireHTTP`, `rpc.invoke`. */
|
|
37
|
+
const enclosingCall = (node) => {
|
|
38
|
+
for (let current = node.parent; current; current = current.parent) {
|
|
39
|
+
if (ts.isCallExpression(current) &&
|
|
40
|
+
(ts.isIdentifier(current.expression) ||
|
|
41
|
+
ts.isPropertyAccessExpression(current.expression))) {
|
|
42
|
+
return current.expression.getText();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Records a function declared with `disabled`, in place of everything
|
|
49
|
+
* addFunctions would otherwise record. A function defined inline in what
|
|
50
|
+
* uses it is referenced by that, so it is refused instead.
|
|
51
|
+
*/
|
|
52
|
+
export const recordDisabledFunction = (logger, state, node, pikkuFuncId, meta) => {
|
|
53
|
+
const parent = node.parent;
|
|
54
|
+
if (ts.isVariableDeclaration(parent) && parent.initializer === node) {
|
|
55
|
+
state.functions.disabled[pikkuFuncId] = meta;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const call = enclosingCall(node);
|
|
59
|
+
logger.critical(ErrorCode.DISABLED_FUNCTION_REFERENCED, `A function declared \`disabled\` is defined inline at ${position(state, node)}` +
|
|
60
|
+
(call ? ` inside ${call}(...)` : '') +
|
|
61
|
+
`, which still uses it. Remove what uses it, or remove \`disabled\`.`);
|
|
62
|
+
};
|
|
63
|
+
const byLocalName = new WeakMap();
|
|
64
|
+
/** Disabled functions by the name code reaches them by: the export, else the name. */
|
|
65
|
+
const disabledByLocalName = (state) => {
|
|
66
|
+
let index = byLocalName.get(state);
|
|
67
|
+
if (index)
|
|
68
|
+
return index;
|
|
69
|
+
index = new Map();
|
|
70
|
+
for (const meta of Object.values(state.functions.disabled)) {
|
|
71
|
+
const local = meta.exportedName ?? meta.name;
|
|
72
|
+
index.set(local, [...(index.get(local) ?? []), meta]);
|
|
73
|
+
}
|
|
74
|
+
byLocalName.set(state, index);
|
|
75
|
+
return index;
|
|
76
|
+
};
|
|
77
|
+
/** Whether a node is inside the definition of a disabled function, whose body is dead too. */
|
|
78
|
+
const isInsideDisabled = (state, node) => {
|
|
79
|
+
const fileName = node.getSourceFile().fileName;
|
|
80
|
+
const index = disabledByLocalName(state);
|
|
81
|
+
for (let current = node.parent; current; current = current.parent) {
|
|
82
|
+
if (ts.isVariableDeclaration(current) &&
|
|
83
|
+
ts.isIdentifier(current.name) &&
|
|
84
|
+
index.get(current.name.text)?.some((meta) => meta.sourceFile === fileName)) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
};
|
|
90
|
+
const isNotAReference = (node) => {
|
|
91
|
+
const parent = node.parent;
|
|
92
|
+
if (ts.isImportSpecifier(parent) ||
|
|
93
|
+
ts.isExportSpecifier(parent) ||
|
|
94
|
+
ts.isImportClause(parent) ||
|
|
95
|
+
ts.isNamespaceImport(parent) ||
|
|
96
|
+
ts.isTypeQueryNode(parent) ||
|
|
97
|
+
ts.isQualifiedName(parent)) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
if (ts.isShorthandPropertyAssignment(parent))
|
|
101
|
+
return false;
|
|
102
|
+
return 'name' in parent && parent.name === node;
|
|
103
|
+
};
|
|
104
|
+
const resolveDeclaration = (node, checker) => {
|
|
105
|
+
let symbol = ts.isShorthandPropertyAssignment(node.parent)
|
|
106
|
+
? checker.getShorthandAssignmentValueSymbol(node.parent)
|
|
107
|
+
: checker.getSymbolAtLocation(node);
|
|
108
|
+
if (symbol && symbol.flags & ts.SymbolFlags.Alias) {
|
|
109
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
110
|
+
}
|
|
111
|
+
return symbol?.valueDeclaration ?? symbol?.declarations?.[0];
|
|
112
|
+
};
|
|
113
|
+
/** `rpc.invoke('x')` / `rpc.mesh('x')`, seen through `!` and parentheses. */
|
|
114
|
+
const invokedRpcName = (node) => {
|
|
115
|
+
const callee = node.expression;
|
|
116
|
+
if (!ts.isPropertyAccessExpression(callee) ||
|
|
117
|
+
(callee.name.text !== 'invoke' && callee.name.text !== 'mesh')) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
let receiver = callee.expression;
|
|
121
|
+
while (ts.isNonNullExpression(receiver) ||
|
|
122
|
+
ts.isParenthesizedExpression(receiver)) {
|
|
123
|
+
receiver = receiver.expression;
|
|
124
|
+
}
|
|
125
|
+
const isRpc = ts.isIdentifier(receiver)
|
|
126
|
+
? receiver.text === 'rpc'
|
|
127
|
+
: ts.isPropertyAccessExpression(receiver) && receiver.name.text === 'rpc';
|
|
128
|
+
const [first] = node.arguments;
|
|
129
|
+
if (!isRpc ||
|
|
130
|
+
!first ||
|
|
131
|
+
!(ts.isStringLiteral(first) || ts.isNoSubstitutionTemplateLiteral(first))) {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
return first.text;
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Whether a node sits in a generated file. This run rewrites those from its own
|
|
138
|
+
* state, which leaves disabled functions out, so a reference there is the last
|
|
139
|
+
* run's and is about to go.
|
|
140
|
+
*/
|
|
141
|
+
const isGenerated = (node) => {
|
|
142
|
+
const fileName = node.getSourceFile().fileName;
|
|
143
|
+
return fileName.endsWith('.gen.ts') || fileName.endsWith('.gen.js');
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* PKU562: anything that still references a disabled function — a wiring, an
|
|
147
|
+
* agent's tools, an `rpc.invoke` by name, any other code. Runs in the routes
|
|
148
|
+
* sweep, after every function is registered, so it sees the whole set.
|
|
149
|
+
*/
|
|
150
|
+
export const checkDisabledReferences = (logger, node, checker, state) => {
|
|
151
|
+
if (Object.keys(state.functions.disabled).length === 0)
|
|
152
|
+
return;
|
|
153
|
+
if (isGenerated(node))
|
|
154
|
+
return;
|
|
155
|
+
if (ts.isCallExpression(node)) {
|
|
156
|
+
const invoked = invokedRpcName(node);
|
|
157
|
+
if (invoked === undefined)
|
|
158
|
+
return;
|
|
159
|
+
const meta = Object.values(state.functions.disabled).find((m) => m.name === invoked);
|
|
160
|
+
if (!meta || isInsideDisabled(state, node))
|
|
161
|
+
return;
|
|
162
|
+
logger.critical(ErrorCode.DISABLED_FUNCTION_REFERENCED, `${describe(state, meta)}, but ${node.expression.getText()}('${invoked}') calls it at ${position(state, node)}. ` +
|
|
163
|
+
`Remove the call, or remove \`disabled\`.`);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (!ts.isIdentifier(node))
|
|
167
|
+
return;
|
|
168
|
+
const candidates = disabledByLocalName(state).get(node.text);
|
|
169
|
+
if (!candidates || isNotAReference(node))
|
|
170
|
+
return;
|
|
171
|
+
const decl = resolveDeclaration(node, checker);
|
|
172
|
+
if (!decl || !ts.isVariableDeclaration(decl) || !ts.isIdentifier(decl.name)) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const declFile = decl.getSourceFile().fileName;
|
|
176
|
+
const declName = decl.name.text;
|
|
177
|
+
const meta = candidates.find((m) => m.sourceFile === declFile && (m.exportedName ?? m.name) === declName);
|
|
178
|
+
if (!meta || isInsideDisabled(state, node))
|
|
179
|
+
return;
|
|
180
|
+
const call = enclosingCall(node);
|
|
181
|
+
logger.critical(ErrorCode.DISABLED_FUNCTION_REFERENCED, `${describe(state, meta)}, but ` +
|
|
182
|
+
(call ? `${call}(...) references it` : 'it is referenced') +
|
|
183
|
+
` at ${position(state, node)}. Remove the reference, or remove \`disabled\`.`);
|
|
184
|
+
};
|
|
185
|
+
/** Every `rpcName` in a workflow's meta, wherever in its steps it sits. */
|
|
186
|
+
const collectRpcNames = (value, into) => {
|
|
187
|
+
if (Array.isArray(value)) {
|
|
188
|
+
for (const item of value)
|
|
189
|
+
collectRpcNames(item, into);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (!value || typeof value !== 'object')
|
|
193
|
+
return;
|
|
194
|
+
for (const [key, child] of Object.entries(value)) {
|
|
195
|
+
if (key === 'rpcName' && typeof child === 'string')
|
|
196
|
+
into.add(child);
|
|
197
|
+
else
|
|
198
|
+
collectRpcNames(child, into);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* PKU562 for workflow steps, which name the function they call as a string
|
|
203
|
+
* the workflow meta already holds. Runs once, after the routes sweep.
|
|
204
|
+
*/
|
|
205
|
+
export const checkDisabledWorkflowSteps = (logger, state) => {
|
|
206
|
+
const disabled = Object.values(state.functions.disabled);
|
|
207
|
+
if (disabled.length === 0)
|
|
208
|
+
return;
|
|
209
|
+
const sections = [
|
|
210
|
+
['workflow', state.workflows.meta],
|
|
211
|
+
['workflow graph', state.workflows.graphMeta],
|
|
212
|
+
];
|
|
213
|
+
for (const [kind, section] of sections) {
|
|
214
|
+
for (const [workflowName, meta] of Object.entries(section)) {
|
|
215
|
+
const names = new Set();
|
|
216
|
+
collectRpcNames(meta, names);
|
|
217
|
+
for (const rpcName of [...names].sort()) {
|
|
218
|
+
const target = disabled.find((m) => m.name === rpcName);
|
|
219
|
+
if (!target)
|
|
220
|
+
continue;
|
|
221
|
+
logger.critical(ErrorCode.DISABLED_FUNCTION_REFERENCED, `${describe(state, target)}, but ${kind} '${workflowName}' calls it as a step. ` +
|
|
222
|
+
`Remove the step, or remove \`disabled\`.`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
};
|
package/dist/error-codes.d.ts
CHANGED
|
@@ -46,6 +46,17 @@ export declare enum ErrorCode {
|
|
|
46
46
|
REMOTE_ADDON_SDK_CORE_MISMATCH = "PKU351",
|
|
47
47
|
REMOTE_ADDON_SDK_INTEGRITY_MISMATCH = "PKU352",
|
|
48
48
|
REMOTE_ADDON_PACKAGE_INVALID = "PKU353",
|
|
49
|
+
ADDON_DEPENDENCY_VERSION_MISMATCH = "PKU354",
|
|
50
|
+
ADDON_USES_NOT_DECLARED = "PKU355",
|
|
51
|
+
ADDON_DEPENDENCY_INVALID = "PKU356",
|
|
52
|
+
STUDIO_SCREEN_PATH_DUPLICATE = "PKU357",
|
|
53
|
+
STUDIO_MESSAGE_KEY_MISSING = "PKU358",
|
|
54
|
+
STUDIO_BADGE_FUNCTION_NOT_FOUND = "PKU359",
|
|
55
|
+
CONTRIBUTORS_NOT_STATIC = "PKU360",
|
|
56
|
+
CONTRIBUTOR_NAME_DUPLICATED = "PKU361",
|
|
57
|
+
ADDON_CONTRIBUTORS_NOT_STATIC = "PKU362",
|
|
58
|
+
ADDON_CONTRIBUTOR_UNKNOWN = "PKU363",
|
|
59
|
+
ADDON_LEGACY_CONTRIBUTORS = "PKU364",
|
|
49
60
|
DB_COLUMN_NAME_TYPE_CONTRADICTION = "PKU480",
|
|
50
61
|
DB_JSON_COLUMN_UNTYPED = "PKU481",
|
|
51
62
|
DB_FORMAT_ON_NON_STRING = "PKU482",
|
|
@@ -56,6 +67,9 @@ export declare enum ErrorCode {
|
|
|
56
67
|
SCHEMA_REFERENCE_UNRESOLVED = "PKU463",
|
|
57
68
|
INLINE_SCHEMA = "PKU489",
|
|
58
69
|
FUNCTION_METADATA_NOT_FOUND = "PKU559",
|
|
70
|
+
DISABLED_FUNCTION_REFERENCED = "PKU562",
|
|
71
|
+
UNUSED_FUNCTION = "PKU563",
|
|
72
|
+
FACTORY_NOT_A_DIRECT_CALL = "PKU564",
|
|
59
73
|
DUPLICATE_AUTH_DEFINITION = "PKU581",
|
|
60
74
|
AUTH_NOT_EXPORTED = "PKU582",
|
|
61
75
|
DUPLICATE_VIRTUAL_USERS_DEFINITION = "PKU585",
|
package/dist/error-codes.js
CHANGED
|
@@ -49,6 +49,17 @@ export var ErrorCode;
|
|
|
49
49
|
ErrorCode["REMOTE_ADDON_SDK_CORE_MISMATCH"] = "PKU351";
|
|
50
50
|
ErrorCode["REMOTE_ADDON_SDK_INTEGRITY_MISMATCH"] = "PKU352";
|
|
51
51
|
ErrorCode["REMOTE_ADDON_PACKAGE_INVALID"] = "PKU353";
|
|
52
|
+
ErrorCode["ADDON_DEPENDENCY_VERSION_MISMATCH"] = "PKU354";
|
|
53
|
+
ErrorCode["ADDON_USES_NOT_DECLARED"] = "PKU355";
|
|
54
|
+
ErrorCode["ADDON_DEPENDENCY_INVALID"] = "PKU356";
|
|
55
|
+
ErrorCode["STUDIO_SCREEN_PATH_DUPLICATE"] = "PKU357";
|
|
56
|
+
ErrorCode["STUDIO_MESSAGE_KEY_MISSING"] = "PKU358";
|
|
57
|
+
ErrorCode["STUDIO_BADGE_FUNCTION_NOT_FOUND"] = "PKU359";
|
|
58
|
+
ErrorCode["CONTRIBUTORS_NOT_STATIC"] = "PKU360";
|
|
59
|
+
ErrorCode["CONTRIBUTOR_NAME_DUPLICATED"] = "PKU361";
|
|
60
|
+
ErrorCode["ADDON_CONTRIBUTORS_NOT_STATIC"] = "PKU362";
|
|
61
|
+
ErrorCode["ADDON_CONTRIBUTOR_UNKNOWN"] = "PKU363";
|
|
62
|
+
ErrorCode["ADDON_LEGACY_CONTRIBUTORS"] = "PKU364";
|
|
52
63
|
// Database schema codegen warnings
|
|
53
64
|
ErrorCode["DB_COLUMN_NAME_TYPE_CONTRADICTION"] = "PKU480";
|
|
54
65
|
ErrorCode["DB_JSON_COLUMN_UNTYPED"] = "PKU481";
|
|
@@ -62,6 +73,9 @@ export var ErrorCode;
|
|
|
62
73
|
ErrorCode["INLINE_SCHEMA"] = "PKU489";
|
|
63
74
|
// Function errors
|
|
64
75
|
ErrorCode["FUNCTION_METADATA_NOT_FOUND"] = "PKU559";
|
|
76
|
+
ErrorCode["DISABLED_FUNCTION_REFERENCED"] = "PKU562";
|
|
77
|
+
ErrorCode["UNUSED_FUNCTION"] = "PKU563";
|
|
78
|
+
ErrorCode["FACTORY_NOT_A_DIRECT_CALL"] = "PKU564";
|
|
65
79
|
// Auth errors
|
|
66
80
|
ErrorCode["DUPLICATE_AUTH_DEFINITION"] = "PKU581";
|
|
67
81
|
ErrorCode["AUTH_NOT_EXPORTED"] = "PKU582";
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { inspect, analyzeProgram } from './inspector.js';
|
|
|
2
2
|
export { getInitialInspectorState } from './initial-state.js';
|
|
3
3
|
export { finalizeInspectorState } from './finalize.js';
|
|
4
4
|
export type { TypesMap } from './types-map.js';
|
|
5
|
-
export type { AddonConfig, AuthDefinition, CredentialOverrideMeta,
|
|
5
|
+
export type { AddonConfig, AuthDefinition, CredentialOverrideMeta, StudioMeta, StudioScreenMeta, StudioNavMeta, ContributorRef, ContributorVariantsMeta, ContributorsManifestMeta, OptedInContributorsMeta, InspectorDiagnostic, InspectorFilters, InspectorHTTPState, InspectorLogger, InspectorMiddlewareState, InspectorOptions, InspectorPermissionState, InspectorServiceTypes, InspectorState, MetaInputTypes, MiddlewareGroupMeta, MiddlewareRegistration, SchemaRef, } from './types.js';
|
|
6
6
|
export type { FilesAndMethodsErrors } from './utils/get-files-and-methods.js';
|
|
7
7
|
export { ErrorCode } from './error-codes.js';
|
|
8
8
|
export type { DiagnosticSeverity, CodedDiagnostic } from './error-codes.js';
|
|
@@ -16,6 +16,7 @@ export { incompatibleServicesFor, resolveDeployTarget, } from './utils/resolve-d
|
|
|
16
16
|
export { generateCustomTypes, customTypeNames, sanitizeTypeName, } from './utils/custom-types-generator.js';
|
|
17
17
|
export type { GenerateCustomTypesOptions } from './utils/custom-types-generator.js';
|
|
18
18
|
export { unresolvedSchemaReferences } from './utils/post-process.js';
|
|
19
|
+
export { findUnusedFunctions, type UnusedFunction, } from './utils/find-unused-functions.js';
|
|
19
20
|
export type { SurfaceUsageCounts } from './utils/extract-surface-usage.js';
|
|
20
21
|
export { createEmptyManifest, serializeManifest, } from './utils/contract-hashes.js';
|
|
21
22
|
export type { ContractEntry, VersionHashEntry, VersionValidateError, VersionManifest, VersionManifestEntry, } from './utils/contract-hashes.js';
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export { filterInspectorState } from './utils/filter-inspector-state.js';
|
|
|
9
9
|
export { incompatibleServicesFor, resolveDeployTarget, } from './utils/resolve-deploy-target.js';
|
|
10
10
|
export { generateCustomTypes, customTypeNames, sanitizeTypeName, } from './utils/custom-types-generator.js';
|
|
11
11
|
export { unresolvedSchemaReferences } from './utils/post-process.js';
|
|
12
|
+
export { findUnusedFunctions, } from './utils/find-unused-functions.js';
|
|
12
13
|
export { createEmptyManifest, serializeManifest, } from './utils/contract-hashes.js';
|
|
13
14
|
export { serializeMCPJson } from './utils/serialize-mcp-json.js';
|
|
14
15
|
export { deserializeDslWorkflow, deserializeGraphWorkflow, deserializeAllDslWorkflows, } from './utils/workflow/dsl/deserialize-dsl-workflow.js';
|
package/dist/initial-state.js
CHANGED
package/dist/inspector.js
CHANGED
|
@@ -8,6 +8,7 @@ import { createNestedProjectFilter } from './utils/nested-project-filter.js';
|
|
|
8
8
|
import { toProgramPath } from './utils/program-path.js';
|
|
9
9
|
import { resolveLatestVersions } from './utils/resolve-versions.js';
|
|
10
10
|
import { validateWebhookSourceTriggers } from './add/add-trigger.js';
|
|
11
|
+
import { validatePikkuStudio } from './add/add-pikku-studio.js';
|
|
11
12
|
import { generateAllSchemas } from './utils/schema-generator.js';
|
|
12
13
|
import { extractSecretUsage } from './utils/extract-secret-usage.js';
|
|
13
14
|
import { accumulateSurfaceUsage, surfaceUsageArea, } from './utils/extract-surface-usage.js';
|
|
@@ -15,6 +16,7 @@ import { loadAddonFunctionsMeta } from './utils/load-addon-functions-meta.js';
|
|
|
15
16
|
import { resolveServiceTypes } from './utils/resolve-service-types.js';
|
|
16
17
|
import { finalizeInspectorState } from './finalize.js';
|
|
17
18
|
import { getInitialInspectorState } from './initial-state.js';
|
|
19
|
+
import { checkDisabledWorkflowSteps } from './add/check-disabled-references.js';
|
|
18
20
|
export { getInitialInspectorState };
|
|
19
21
|
/**
|
|
20
22
|
* The path mappings the inspector's program has to share with the project.
|
|
@@ -140,7 +142,9 @@ export const analyzeProgram = async (logger, routeFiles, options = {}) => {
|
|
|
140
142
|
ts.forEachChild(sourceFile, (child) => visitRoutes(logger, checker, child, state, sourceOptions));
|
|
141
143
|
}
|
|
142
144
|
logger.debug(`Visit routes phase completed in ${(performance.now() - startRoutes).toFixed(0)}ms`);
|
|
145
|
+
checkDisabledWorkflowSteps(logger, state);
|
|
143
146
|
validateWebhookSourceTriggers(logger, state);
|
|
147
|
+
validatePikkuStudio(logger, state);
|
|
144
148
|
resolveLatestVersions(state, logger);
|
|
145
149
|
if (options.schemaConfig) {
|
|
146
150
|
const startSchemas = performance.now();
|
package/dist/lite.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export { filterInspectorState } from './utils/filter-inspector-state.js';
|
|
|
19
19
|
export { incompatibleServicesFor, resolveDeployTarget, } from './utils/resolve-deploy-target.js';
|
|
20
20
|
export { customTypeNames, generateCustomTypes, sanitizeTypeName, } from './utils/custom-types-generator.js';
|
|
21
21
|
export { unresolvedSchemaReferences } from './utils/post-process.js';
|
|
22
|
+
export { findUnusedFunctions, type UnusedFunction, } from './utils/find-unused-functions.js';
|
|
22
23
|
export { computeContractHash, createEmptyManifest, serializeManifest, } from './utils/contract-hashes.js';
|
|
23
24
|
export { serializeMCPJson } from './utils/serialize-mcp-json.js';
|
|
24
25
|
export { deserializeDslWorkflow, deserializeGraphWorkflow, deserializeAllDslWorkflows, } from './utils/workflow/dsl/deserialize-dsl-workflow.js';
|
|
@@ -27,3 +28,5 @@ export { resolveCoreType } from './utils/resolve-core-type.js';
|
|
|
27
28
|
export { addonResolutionDirs, createAddonResolver, } from './utils/addon-resolution.js';
|
|
28
29
|
export { ADDONS_LOCK_FILE, REMOTE_SDK_DIR, SDK_INTEGRITY_MARKER, checkLockedSdk, emptyAddonsLock, parseRemoteAddonPackage, readAddonsLock, readSdkManifest, remoteSdkDir, serializeAddonsLock, } from './utils/remote-addon-sdk.js';
|
|
29
30
|
export type { AddonsLock, AddonsLockEntry, RemoteAddonSdkManifest, RemoteAddonPackageSpec, LockedSdk, } from './utils/remote-addon-sdk.js';
|
|
31
|
+
export { declaredAddonNames, findInstalledPackageDir, parseAddonDependencies, readAddonDependencies, } from './utils/addon-dependencies.js';
|
|
32
|
+
export type { AddonDependency, AddonDependencies, } from './utils/addon-dependencies.js';
|
package/dist/lite.js
CHANGED
|
@@ -18,6 +18,7 @@ export { filterInspectorState } from './utils/filter-inspector-state.js';
|
|
|
18
18
|
export { incompatibleServicesFor, resolveDeployTarget, } from './utils/resolve-deploy-target.js';
|
|
19
19
|
export { customTypeNames, generateCustomTypes, sanitizeTypeName, } from './utils/custom-types-generator.js';
|
|
20
20
|
export { unresolvedSchemaReferences } from './utils/post-process.js';
|
|
21
|
+
export { findUnusedFunctions, } from './utils/find-unused-functions.js';
|
|
21
22
|
export { computeContractHash, createEmptyManifest, serializeManifest, } from './utils/contract-hashes.js';
|
|
22
23
|
export { serializeMCPJson } from './utils/serialize-mcp-json.js';
|
|
23
24
|
export { deserializeDslWorkflow, deserializeGraphWorkflow, deserializeAllDslWorkflows, } from './utils/workflow/dsl/deserialize-dsl-workflow.js';
|
|
@@ -25,3 +26,4 @@ export { resolveFunctionMeta } from './utils/resolve-function-meta.js';
|
|
|
25
26
|
export { resolveCoreType } from './utils/resolve-core-type.js';
|
|
26
27
|
export { addonResolutionDirs, createAddonResolver, } from './utils/addon-resolution.js';
|
|
27
28
|
export { ADDONS_LOCK_FILE, REMOTE_SDK_DIR, SDK_INTEGRITY_MARKER, checkLockedSdk, emptyAddonsLock, parseRemoteAddonPackage, readAddonsLock, readSdkManifest, remoteSdkDir, serializeAddonsLock, } from './utils/remote-addon-sdk.js';
|
|
29
|
+
export { declaredAddonNames, findInstalledPackageDir, parseAddonDependencies, readAddonDependencies, } from './utils/addon-dependencies.js';
|
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 { AddonDependency } from './utils/addon-dependencies.js';
|
|
3
4
|
import type { SurfaceUsageCounts } from './utils/extract-surface-usage.js';
|
|
4
5
|
import type { ChannelMessageMeta, ChannelsMeta } from '@pikku/core/channel';
|
|
5
6
|
import type { GatewaysMeta } from '@pikku/core/gateway';
|
|
@@ -105,6 +106,19 @@ export interface InspectorFunctionState {
|
|
|
105
106
|
* inspection pass — deliberately not part of the serialized function meta.
|
|
106
107
|
*/
|
|
107
108
|
dynamicImportIds: Set<string>;
|
|
109
|
+
/**
|
|
110
|
+
* Functions declared with `disabled`, keyed by pikkuFuncId. They are in no
|
|
111
|
+
* other part of the state: nothing is generated for them, and a reference
|
|
112
|
+
* to one is PKU562.
|
|
113
|
+
*/
|
|
114
|
+
disabled: Record<string, DisabledFunctionMeta>;
|
|
115
|
+
}
|
|
116
|
+
export interface DisabledFunctionMeta {
|
|
117
|
+
name: string;
|
|
118
|
+
sourceFile: string;
|
|
119
|
+
exportedName?: string;
|
|
120
|
+
/** The string `disabled` was given, when it was not just `true`. */
|
|
121
|
+
reason?: string;
|
|
108
122
|
}
|
|
109
123
|
export interface InspectorChannelState {
|
|
110
124
|
meta: ChannelsMeta;
|
|
@@ -517,11 +531,22 @@ export type CredentialOverrideMeta = string | {
|
|
|
517
531
|
name?: string;
|
|
518
532
|
mode?: 'singleton' | 'wire';
|
|
519
533
|
};
|
|
520
|
-
/** A screen
|
|
521
|
-
export type
|
|
534
|
+
/** A screen's nav entry as `definePlugin` declares it, read statically. */
|
|
535
|
+
export type StudioNavMeta = {
|
|
536
|
+
area: 'rail' | 'team' | 'project' | 'account';
|
|
537
|
+
icon: string;
|
|
538
|
+
order: number;
|
|
539
|
+
badge?: string;
|
|
540
|
+
};
|
|
541
|
+
/** A screen of an addon as `definePlugin` declares it, read statically. */
|
|
542
|
+
export type StudioScreenMeta = {
|
|
522
543
|
path: string;
|
|
523
544
|
title: string;
|
|
524
|
-
|
|
545
|
+
scope: 'studio' | 'team' | 'project';
|
|
546
|
+
nav?: StudioNavMeta;
|
|
547
|
+
replaces?: boolean;
|
|
548
|
+
needsApp?: boolean;
|
|
549
|
+
layout?: 'bare';
|
|
525
550
|
scopes?: string[];
|
|
526
551
|
} & ({
|
|
527
552
|
component: string;
|
|
@@ -530,20 +555,42 @@ export type AddonScreenMeta = {
|
|
|
530
555
|
app: string;
|
|
531
556
|
component?: undefined;
|
|
532
557
|
});
|
|
533
|
-
export type
|
|
558
|
+
export type StudioMeta = {
|
|
534
559
|
title: string;
|
|
535
|
-
|
|
536
|
-
/** The source file whose `defineScreens` call declared this manifest. */
|
|
560
|
+
/** The source file whose `definePlugin` call declared this manifest. */
|
|
537
561
|
file: string;
|
|
538
|
-
screens:
|
|
562
|
+
screens: StudioScreenMeta[];
|
|
539
563
|
/**
|
|
540
564
|
* The addon's role: every scope its own functions require, derived by
|
|
541
|
-
* `pikku all` and never declared. Absent from the raw `
|
|
565
|
+
* `pikku all` and never declared. Absent from the raw `definePlugin` read.
|
|
542
566
|
* On a wired instance it also carries the host's `wireAddon` scopes, which
|
|
543
567
|
* the runner demands of every function in the package.
|
|
544
568
|
*/
|
|
545
569
|
scopes?: string[];
|
|
546
570
|
};
|
|
571
|
+
/**
|
|
572
|
+
* Where a contributor variant lives, so a host can import only that variant.
|
|
573
|
+
* `module` is the source file that declares it (absolute while it is in
|
|
574
|
+
* inspector state, relative to the addon package in the published meta);
|
|
575
|
+
* `export` is the name that module exports it under.
|
|
576
|
+
*/
|
|
577
|
+
export type ContributorRef = {
|
|
578
|
+
module: string;
|
|
579
|
+
export: string;
|
|
580
|
+
};
|
|
581
|
+
/** One `defineContributors` entry: each variant optional, each its own import. */
|
|
582
|
+
export type ContributorVariantsMeta = {
|
|
583
|
+
dev?: ContributorRef;
|
|
584
|
+
deploy?: ContributorRef;
|
|
585
|
+
};
|
|
586
|
+
/** The contributors an addon registers, keyed by name. */
|
|
587
|
+
export type ContributorsManifestMeta = Record<string, ContributorVariantsMeta>;
|
|
588
|
+
/** The contributors a project opted in to for one wired addon. */
|
|
589
|
+
export type OptedInContributorsMeta = {
|
|
590
|
+
packageName: string;
|
|
591
|
+
/** Each opted-in name with the variants the addon recorded; `module` is relative to the addon package. */
|
|
592
|
+
contributors: ContributorsManifestMeta;
|
|
593
|
+
};
|
|
547
594
|
export interface InspectorServiceTypes {
|
|
548
595
|
/** Property names of the project's SingletonServices type, sorted. */
|
|
549
596
|
singletonServices: string[] | null;
|
|
@@ -552,26 +599,15 @@ export interface InspectorServiceTypes {
|
|
|
552
599
|
/** Singleton services that are a `SecretService` under another name. */
|
|
553
600
|
secretAliases: string[];
|
|
554
601
|
}
|
|
555
|
-
export interface AddonContributorRef {
|
|
556
|
-
module: string;
|
|
557
|
-
export?: string;
|
|
558
|
-
}
|
|
559
|
-
export interface AddonContributorsMeta {
|
|
560
|
-
packageName: string;
|
|
561
|
-
packageDir: string;
|
|
562
|
-
contributors: AddonContributorRef[];
|
|
563
|
-
}
|
|
564
602
|
export interface InspectorState {
|
|
565
|
-
/** Set when this package calls `
|
|
566
|
-
|
|
567
|
-
/** The
|
|
568
|
-
|
|
569
|
-
/**
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
*/
|
|
574
|
-
addonContributors?: Record<string, AddonContributorsMeta>;
|
|
603
|
+
/** Set when this package calls `definePlugin`: the screens it ships. */
|
|
604
|
+
studio?: StudioMeta;
|
|
605
|
+
/** The `definePlugin` metadata of the addons this project wires with `ui: true`, by namespace. */
|
|
606
|
+
addonStudio?: Record<string, StudioMeta>;
|
|
607
|
+
/** Set when this package calls `defineContributors`: the contributors it registers, by name. */
|
|
608
|
+
contributorsManifest?: ContributorsManifestMeta;
|
|
609
|
+
/** Per wired addon namespace, the contributors `wireAddon({ contributors })` opted in to. */
|
|
610
|
+
optedInContributors?: Record<string, OptedInContributorsMeta>;
|
|
575
611
|
rootDir: string;
|
|
576
612
|
singletonServicesTypeImportMap: PathToNameAndType;
|
|
577
613
|
wireServicesTypeImportMap: PathToNameAndType;
|
|
@@ -688,8 +724,20 @@ export interface InspectorState {
|
|
|
688
724
|
/** Declared with `ui: true`: the package must ship screens, and the host mounts them. */
|
|
689
725
|
ui?: boolean;
|
|
690
726
|
rpcEndpoint?: string;
|
|
691
|
-
/**
|
|
727
|
+
/**
|
|
728
|
+
* The other addons this instance may call: the name the addon calls
|
|
729
|
+
* (its declared alias, or the package name) -> the name the target is
|
|
730
|
+
* wired under in this app.
|
|
731
|
+
*/
|
|
692
732
|
uses?: Record<string, string>;
|
|
733
|
+
/**
|
|
734
|
+
* The names the addon's package.json declares under `pikku.uses`, in
|
|
735
|
+
* either form. Read when its meta loads; absent when it declares
|
|
736
|
+
* nothing, which leaves `uses` unchecked against a declaration.
|
|
737
|
+
*/
|
|
738
|
+
declaredUses?: string[];
|
|
739
|
+
/** The `{ alias: 'package@range' }` form of `pikku.uses`, per alias. */
|
|
740
|
+
dependencies?: AddonDependency[];
|
|
693
741
|
/**
|
|
694
742
|
* `true` offers every function the addon declared `mcp: true`; a list
|
|
695
743
|
* names the functions to offer, whatever the addon declared. Absent
|
|
@@ -757,6 +805,8 @@ export interface InspectorState {
|
|
|
757
805
|
* `ContentService` instead of one scoped to its own folder.
|
|
758
806
|
*/
|
|
759
807
|
globalContent?: string;
|
|
808
|
+
/** The addon contributor names this project opted in to, as `wireAddon({ contributors })` listed them. */
|
|
809
|
+
contributors?: string[];
|
|
760
810
|
}>;
|
|
761
811
|
wireAddonFiles: Set<string>;
|
|
762
812
|
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One addon an addon calls: the alias its code calls it by
|
|
3
|
+
* (`rpc.invoke('<alias>:<fn>')`) and the package and semver range it was
|
|
4
|
+
* written against.
|
|
5
|
+
*/
|
|
6
|
+
export type AddonDependency = {
|
|
7
|
+
alias: string;
|
|
8
|
+
package: string;
|
|
9
|
+
range: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* What an addon's package.json declares under `pikku.uses`.
|
|
13
|
+
*
|
|
14
|
+
* - `none`: nothing. The wiring's `uses` is not checked against a declaration.
|
|
15
|
+
* - `list`: the older form, a list of names the addon calls. The name is also
|
|
16
|
+
* the package the wiring must map it to.
|
|
17
|
+
* - `map`: `{ "<alias>": "<package>@<range>" }`. `invalid` holds a message for
|
|
18
|
+
* each entry that could not be read; those entries are not in `dependencies`.
|
|
19
|
+
*
|
|
20
|
+
* knowledge: decisions/an-addon-declares-the-addons-it-calls.md
|
|
21
|
+
*/
|
|
22
|
+
export type AddonDependencies = {
|
|
23
|
+
form: 'none';
|
|
24
|
+
} | {
|
|
25
|
+
form: 'list';
|
|
26
|
+
names: string[];
|
|
27
|
+
} | {
|
|
28
|
+
form: 'map';
|
|
29
|
+
dependencies: AddonDependency[];
|
|
30
|
+
invalid: string[];
|
|
31
|
+
};
|
|
32
|
+
/** The names a wiring's `uses` may map: the declared aliases, or the listed names. */
|
|
33
|
+
export declare const declaredAddonNames: (declared: AddonDependencies) => string[];
|
|
34
|
+
/** Reads the `pikku.uses` value of an addon's package.json. */
|
|
35
|
+
export declare const parseAddonDependencies: (uses: unknown) => AddonDependencies;
|
|
36
|
+
/** The `pikku.uses` an addon's package.json declares. `none` when there is no readable package.json. */
|
|
37
|
+
export declare const readAddonDependencies: (packageDir: string) => AddonDependencies;
|
|
38
|
+
/**
|
|
39
|
+
* The directory `packageName` is installed in, looking in `node_modules` from
|
|
40
|
+
* each of `dirs` upwards, as Node's resolution would. Null when it is not
|
|
41
|
+
* installed.
|
|
42
|
+
*/
|
|
43
|
+
export declare const findInstalledPackageDir: (dirs: string[], packageName: string) => string | null;
|