@pikku/inspector 0.12.109 → 0.12.111
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-file-with-factory.d.ts +7 -1
- package/dist/add/add-file-with-factory.js +63 -2
- package/dist/add/add-functions.js +32 -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.d.ts +1 -1
- package/dist/add/add-rpc-invocations.js +14 -2
- package/dist/add/add-wire-remote-addon.js +25 -4
- 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/finalize.js +3 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +3 -0
- package/dist/initial-state.js +1 -0
- package/dist/inspector.js +4 -0
- package/dist/lite.d.ts +7 -1
- package/dist/lite.js +5 -1
- package/dist/types.d.ts +65 -13
- package/dist/utils/addon-dependencies.d.ts +43 -0
- package/dist/utils/addon-dependencies.js +91 -0
- package/dist/utils/contract-hashes.js +2 -2
- package/dist/utils/find-unused-functions.d.ts +20 -0
- package/dist/utils/find-unused-functions.js +132 -0
- package/dist/utils/find-wire-remote-addon-specs.d.ts +17 -0
- package/dist/utils/find-wire-remote-addon-specs.js +53 -0
- package/dist/utils/get-property-value.js +4 -1
- package/dist/utils/load-addon-functions-meta.js +69 -46
- package/dist/utils/post-process.js +58 -8
- package/dist/utils/remote-addon-sdk.d.ts +57 -0
- package/dist/utils/remote-addon-sdk.js +111 -0
- package/dist/utils/resolve-addon-package.d.ts +1 -0
- package/dist/utils/resolve-addon-package.js +1 -1
- package/dist/utils/resolve-versions.js +18 -0
- package/dist/utils/serialize-inspector-state.d.ts +5 -2
- package/dist/utils/serialize-inspector-state.js +12 -4
- package/dist/utils/validate-addon-zod-contract.d.ts +23 -0
- package/dist/utils/validate-addon-zod-contract.js +70 -0
- package/dist/utils/validate-exposed-functions-gated.d.ts +3 -1
- package/dist/utils/validate-exposed-functions-gated.js +15 -8
- package/dist/visit.js +6 -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
|
@@ -42,6 +42,16 @@ export declare enum ErrorCode {
|
|
|
42
42
|
ADDON_USES_NOT_WIRED = "PKU347",
|
|
43
43
|
ADDON_NEEDS_NOT_MAPPED = "PKU348",
|
|
44
44
|
ADDON_CONTENT_GRANT_INVALID = "PKU349",
|
|
45
|
+
REMOTE_ADDON_PACKAGE_NOT_STATIC = "PKU350",
|
|
46
|
+
REMOTE_ADDON_SDK_CORE_MISMATCH = "PKU351",
|
|
47
|
+
REMOTE_ADDON_SDK_INTEGRITY_MISMATCH = "PKU352",
|
|
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",
|
|
45
55
|
DB_COLUMN_NAME_TYPE_CONTRADICTION = "PKU480",
|
|
46
56
|
DB_JSON_COLUMN_UNTYPED = "PKU481",
|
|
47
57
|
DB_FORMAT_ON_NON_STRING = "PKU482",
|
|
@@ -52,6 +62,9 @@ export declare enum ErrorCode {
|
|
|
52
62
|
SCHEMA_REFERENCE_UNRESOLVED = "PKU463",
|
|
53
63
|
INLINE_SCHEMA = "PKU489",
|
|
54
64
|
FUNCTION_METADATA_NOT_FOUND = "PKU559",
|
|
65
|
+
DISABLED_FUNCTION_REFERENCED = "PKU562",
|
|
66
|
+
UNUSED_FUNCTION = "PKU563",
|
|
67
|
+
FACTORY_NOT_A_DIRECT_CALL = "PKU564",
|
|
55
68
|
DUPLICATE_AUTH_DEFINITION = "PKU581",
|
|
56
69
|
AUTH_NOT_EXPORTED = "PKU582",
|
|
57
70
|
DUPLICATE_VIRTUAL_USERS_DEFINITION = "PKU585",
|
|
@@ -89,6 +102,7 @@ export declare enum ErrorCode {
|
|
|
89
102
|
PII_IN_OUTPUT = "PKU910",
|
|
90
103
|
ADDON_WIRING_NOT_ALLOWED = "PKU920",
|
|
91
104
|
ADDON_CONTRACT_HANDLERS_NOT_ALLOWED = "PKU921",
|
|
105
|
+
ADDON_REMOTE_CONTRACT_NOT_ZOD = "PKU922",
|
|
92
106
|
RPC_INVOCATION_TYPE_CAST = "PKU940",
|
|
93
107
|
KNOWLEDGE_NOTE_MISSING = "PKU960",
|
|
94
108
|
SECRET_SERVICE_ALIASED = "PKU950",
|
package/dist/error-codes.js
CHANGED
|
@@ -45,6 +45,16 @@ export var ErrorCode;
|
|
|
45
45
|
ErrorCode["ADDON_USES_NOT_WIRED"] = "PKU347";
|
|
46
46
|
ErrorCode["ADDON_NEEDS_NOT_MAPPED"] = "PKU348";
|
|
47
47
|
ErrorCode["ADDON_CONTENT_GRANT_INVALID"] = "PKU349";
|
|
48
|
+
ErrorCode["REMOTE_ADDON_PACKAGE_NOT_STATIC"] = "PKU350";
|
|
49
|
+
ErrorCode["REMOTE_ADDON_SDK_CORE_MISMATCH"] = "PKU351";
|
|
50
|
+
ErrorCode["REMOTE_ADDON_SDK_INTEGRITY_MISMATCH"] = "PKU352";
|
|
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";
|
|
48
58
|
// Database schema codegen warnings
|
|
49
59
|
ErrorCode["DB_COLUMN_NAME_TYPE_CONTRADICTION"] = "PKU480";
|
|
50
60
|
ErrorCode["DB_JSON_COLUMN_UNTYPED"] = "PKU481";
|
|
@@ -58,6 +68,9 @@ export var ErrorCode;
|
|
|
58
68
|
ErrorCode["INLINE_SCHEMA"] = "PKU489";
|
|
59
69
|
// Function errors
|
|
60
70
|
ErrorCode["FUNCTION_METADATA_NOT_FOUND"] = "PKU559";
|
|
71
|
+
ErrorCode["DISABLED_FUNCTION_REFERENCED"] = "PKU562";
|
|
72
|
+
ErrorCode["UNUSED_FUNCTION"] = "PKU563";
|
|
73
|
+
ErrorCode["FACTORY_NOT_A_DIRECT_CALL"] = "PKU564";
|
|
61
74
|
// Auth errors
|
|
62
75
|
ErrorCode["DUPLICATE_AUTH_DEFINITION"] = "PKU581";
|
|
63
76
|
ErrorCode["AUTH_NOT_EXPORTED"] = "PKU582";
|
|
@@ -114,6 +127,7 @@ export var ErrorCode;
|
|
|
114
127
|
// Addon authoring errors
|
|
115
128
|
ErrorCode["ADDON_WIRING_NOT_ALLOWED"] = "PKU920";
|
|
116
129
|
ErrorCode["ADDON_CONTRACT_HANDLERS_NOT_ALLOWED"] = "PKU921";
|
|
130
|
+
ErrorCode["ADDON_REMOTE_CONTRACT_NOT_ZOD"] = "PKU922";
|
|
117
131
|
ErrorCode["RPC_INVOCATION_TYPE_CAST"] = "PKU940";
|
|
118
132
|
// Knowledge base errors
|
|
119
133
|
ErrorCode["KNOWLEDGE_NOTE_MISSING"] = "PKU960";
|
package/dist/finalize.js
CHANGED
|
@@ -77,7 +77,9 @@ export const finalizeInspectorState = async (logger, analyzed, options = {}) =>
|
|
|
77
77
|
annotateHttpRouteAuth(state);
|
|
78
78
|
// Last of the validators: they read function meta and every wireAddon
|
|
79
79
|
// declaration together, so they need the whole program inspected.
|
|
80
|
-
validateExposedFunctionsGated(logger, state
|
|
80
|
+
validateExposedFunctionsGated(logger, state, {
|
|
81
|
+
isAddon: options.isAddon === true,
|
|
82
|
+
});
|
|
81
83
|
validateTagsResolveToMiddleware(logger, state);
|
|
82
84
|
if (options.openAPI) {
|
|
83
85
|
state.openAPISpec = await generateOpenAPISpec(logger, state.functions.meta, state.http.meta, state.schemas, options.openAPI.additionalInfo, pikkuState(null, 'misc', 'errors'));
|
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, AddonContributorRef, AddonContributorsMeta, 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';
|
|
@@ -30,3 +31,7 @@ export { readModuleSpecifiers } from './utils/read-module-specifiers.js';
|
|
|
30
31
|
export { readJsxLiteralText } from './utils/read-jsx-literal-text.js';
|
|
31
32
|
export { readTsconfigOutDir } from './utils/read-tsconfig-out-dir.js';
|
|
32
33
|
export { addonResolutionDirs, createAddonResolver, } from './utils/addon-resolution.js';
|
|
34
|
+
export { ADDONS_LOCK_FILE, REMOTE_SDK_DIR, SDK_INTEGRITY_MARKER, parseRemoteAddonPackage, emptyAddonsLock, readAddonsLock, serializeAddonsLock, remoteSdkDir, readSdkManifest, checkLockedSdk, } from './utils/remote-addon-sdk.js';
|
|
35
|
+
export { findWireRemoteAddonSpecs } from './utils/find-wire-remote-addon-specs.js';
|
|
36
|
+
export type { AddonsLock, AddonsLockEntry, RemoteAddonSdkManifest, RemoteAddonPackageSpec, LockedSdk, } from './utils/remote-addon-sdk.js';
|
|
37
|
+
export type { WireRemoteAddonSpec } from './utils/find-wire-remote-addon-specs.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';
|
|
@@ -18,3 +19,5 @@ export { readModuleSpecifiers } from './utils/read-module-specifiers.js';
|
|
|
18
19
|
export { readJsxLiteralText } from './utils/read-jsx-literal-text.js';
|
|
19
20
|
export { readTsconfigOutDir } from './utils/read-tsconfig-out-dir.js';
|
|
20
21
|
export { addonResolutionDirs, createAddonResolver, } from './utils/addon-resolution.js';
|
|
22
|
+
export { ADDONS_LOCK_FILE, REMOTE_SDK_DIR, SDK_INTEGRITY_MARKER, parseRemoteAddonPackage, emptyAddonsLock, readAddonsLock, serializeAddonsLock, remoteSdkDir, readSdkManifest, checkLockedSdk, } from './utils/remote-addon-sdk.js';
|
|
23
|
+
export { findWireRemoteAddonSpecs } from './utils/find-wire-remote-addon-specs.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
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* reachable from this file imports `typescript`.
|
|
10
10
|
*/
|
|
11
11
|
export { getInitialInspectorState } from './initial-state.js';
|
|
12
|
+
export { TypesMap } from './types-map.js';
|
|
12
13
|
export { ErrorCode } from './error-codes.js';
|
|
13
14
|
export type { DiagnosticSeverity, CodedDiagnostic } from './error-codes.js';
|
|
14
15
|
export { AUTH_HANDLER_FUNC_ID } from './utils/inspector-constants.js';
|
|
@@ -18,9 +19,14 @@ export { filterInspectorState } from './utils/filter-inspector-state.js';
|
|
|
18
19
|
export { incompatibleServicesFor, resolveDeployTarget, } from './utils/resolve-deploy-target.js';
|
|
19
20
|
export { customTypeNames, generateCustomTypes, sanitizeTypeName, } from './utils/custom-types-generator.js';
|
|
20
21
|
export { unresolvedSchemaReferences } from './utils/post-process.js';
|
|
21
|
-
export {
|
|
22
|
+
export { findUnusedFunctions, type UnusedFunction, } from './utils/find-unused-functions.js';
|
|
23
|
+
export { computeContractHash, createEmptyManifest, serializeManifest, } from './utils/contract-hashes.js';
|
|
22
24
|
export { serializeMCPJson } from './utils/serialize-mcp-json.js';
|
|
23
25
|
export { deserializeDslWorkflow, deserializeGraphWorkflow, deserializeAllDslWorkflows, } from './utils/workflow/dsl/deserialize-dsl-workflow.js';
|
|
24
26
|
export { resolveFunctionMeta } from './utils/resolve-function-meta.js';
|
|
25
27
|
export { resolveCoreType } from './utils/resolve-core-type.js';
|
|
26
28
|
export { addonResolutionDirs, createAddonResolver, } from './utils/addon-resolution.js';
|
|
29
|
+
export { ADDONS_LOCK_FILE, REMOTE_SDK_DIR, SDK_INTEGRITY_MARKER, checkLockedSdk, emptyAddonsLock, parseRemoteAddonPackage, readAddonsLock, readSdkManifest, remoteSdkDir, serializeAddonsLock, } from './utils/remote-addon-sdk.js';
|
|
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
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* reachable from this file imports `typescript`.
|
|
10
10
|
*/
|
|
11
11
|
export { getInitialInspectorState } from './initial-state.js';
|
|
12
|
+
export { TypesMap } from './types-map.js';
|
|
12
13
|
export { ErrorCode } from './error-codes.js';
|
|
13
14
|
export { AUTH_HANDLER_FUNC_ID } from './utils/inspector-constants.js';
|
|
14
15
|
export { SCENARIO_INSTRUMENTATION_FUNCTIONS, isScenarioInstrumentationFunction, } from './add/scenario-instrumentation.js';
|
|
@@ -17,9 +18,12 @@ export { filterInspectorState } from './utils/filter-inspector-state.js';
|
|
|
17
18
|
export { incompatibleServicesFor, resolveDeployTarget, } from './utils/resolve-deploy-target.js';
|
|
18
19
|
export { customTypeNames, generateCustomTypes, sanitizeTypeName, } from './utils/custom-types-generator.js';
|
|
19
20
|
export { unresolvedSchemaReferences } from './utils/post-process.js';
|
|
20
|
-
export {
|
|
21
|
+
export { findUnusedFunctions, } from './utils/find-unused-functions.js';
|
|
22
|
+
export { computeContractHash, createEmptyManifest, serializeManifest, } from './utils/contract-hashes.js';
|
|
21
23
|
export { serializeMCPJson } from './utils/serialize-mcp-json.js';
|
|
22
24
|
export { deserializeDslWorkflow, deserializeGraphWorkflow, deserializeAllDslWorkflows, } from './utils/workflow/dsl/deserialize-dsl-workflow.js';
|
|
23
25
|
export { resolveFunctionMeta } from './utils/resolve-function-meta.js';
|
|
24
26
|
export { resolveCoreType } from './utils/resolve-core-type.js';
|
|
25
27
|
export { addonResolutionDirs, createAddonResolver, } from './utils/addon-resolution.js';
|
|
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';
|
|
@@ -23,6 +24,14 @@ import type { VirtualUserDefinitions } from '@pikku/core/virtual-user';
|
|
|
23
24
|
import type { VariableDefinitions } from '@pikku/core/variable';
|
|
24
25
|
import type { TypesMap } from './types-map.js';
|
|
25
26
|
import type { FunctionsMeta } from '@pikku/core/services';
|
|
27
|
+
/**
|
|
28
|
+
* A remote addon SDK's function meta: each contract function, with the
|
|
29
|
+
* versions of its name the SDK can call and the one a call without `@vN` goes to.
|
|
30
|
+
*/
|
|
31
|
+
export type RemoteAddonFunctionsMeta = Record<string, FunctionsMeta[string] & {
|
|
32
|
+
versions?: number[];
|
|
33
|
+
latestVersion?: number;
|
|
34
|
+
}>;
|
|
26
35
|
import type { FunctionServicesMeta, FunctionWiresMeta } from '@pikku/core/function';
|
|
27
36
|
import type { JSONValue } from '@pikku/core/utils';
|
|
28
37
|
import type { OpenAPISpecInfo } from './utils/serialize-openapi-json.js';
|
|
@@ -97,6 +106,19 @@ export interface InspectorFunctionState {
|
|
|
97
106
|
* inspection pass — deliberately not part of the serialized function meta.
|
|
98
107
|
*/
|
|
99
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;
|
|
100
122
|
}
|
|
101
123
|
export interface InspectorChannelState {
|
|
102
124
|
meta: ChannelsMeta;
|
|
@@ -509,11 +531,22 @@ export type CredentialOverrideMeta = string | {
|
|
|
509
531
|
name?: string;
|
|
510
532
|
mode?: 'singleton' | 'wire';
|
|
511
533
|
};
|
|
512
|
-
/** A screen
|
|
513
|
-
export type
|
|
534
|
+
/** A screen's nav entry as `pikkuStudio` 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 `pikkuStudio` declares it, read statically. */
|
|
542
|
+
export type StudioScreenMeta = {
|
|
514
543
|
path: string;
|
|
515
544
|
title: string;
|
|
516
|
-
|
|
545
|
+
scope: 'studio' | 'team' | 'project';
|
|
546
|
+
nav?: StudioNavMeta;
|
|
547
|
+
replaces?: boolean;
|
|
548
|
+
needsApp?: boolean;
|
|
549
|
+
layout?: 'bare';
|
|
517
550
|
scopes?: string[];
|
|
518
551
|
} & ({
|
|
519
552
|
component: string;
|
|
@@ -522,15 +555,14 @@ export type AddonScreenMeta = {
|
|
|
522
555
|
app: string;
|
|
523
556
|
component?: undefined;
|
|
524
557
|
});
|
|
525
|
-
export type
|
|
558
|
+
export type StudioMeta = {
|
|
526
559
|
title: string;
|
|
527
|
-
|
|
528
|
-
/** The source file whose `defineScreens` call declared this manifest. */
|
|
560
|
+
/** The source file whose `pikkuStudio` call declared this manifest. */
|
|
529
561
|
file: string;
|
|
530
|
-
screens:
|
|
562
|
+
screens: StudioScreenMeta[];
|
|
531
563
|
/**
|
|
532
564
|
* The addon's role: every scope its own functions require, derived by
|
|
533
|
-
* `pikku all` and never declared. Absent from the raw `
|
|
565
|
+
* `pikku all` and never declared. Absent from the raw `pikkuStudio` read.
|
|
534
566
|
* On a wired instance it also carries the host's `wireAddon` scopes, which
|
|
535
567
|
* the runner demands of every function in the package.
|
|
536
568
|
*/
|
|
@@ -554,10 +586,10 @@ export interface AddonContributorsMeta {
|
|
|
554
586
|
contributors: AddonContributorRef[];
|
|
555
587
|
}
|
|
556
588
|
export interface InspectorState {
|
|
557
|
-
/** Set when this package calls `
|
|
558
|
-
|
|
559
|
-
/** The
|
|
560
|
-
|
|
589
|
+
/** Set when this package calls `pikkuStudio`: the screens it ships. */
|
|
590
|
+
studio?: StudioMeta;
|
|
591
|
+
/** The `pikkuStudio` metadata of the addons this project wires with `ui: true`, by namespace. */
|
|
592
|
+
addonStudio?: Record<string, StudioMeta>;
|
|
561
593
|
/**
|
|
562
594
|
* Platform service contributors each wired, non-remote addon declares in its
|
|
563
595
|
* `package.json` under `pikku.contributors`, by namespace. `module` resolves
|
|
@@ -680,8 +712,20 @@ export interface InspectorState {
|
|
|
680
712
|
/** Declared with `ui: true`: the package must ship screens, and the host mounts them. */
|
|
681
713
|
ui?: boolean;
|
|
682
714
|
rpcEndpoint?: string;
|
|
683
|
-
/**
|
|
715
|
+
/**
|
|
716
|
+
* The other addons this instance may call: the name the addon calls
|
|
717
|
+
* (its declared alias, or the package name) -> the name the target is
|
|
718
|
+
* wired under in this app.
|
|
719
|
+
*/
|
|
684
720
|
uses?: Record<string, string>;
|
|
721
|
+
/**
|
|
722
|
+
* The names the addon's package.json declares under `pikku.uses`, in
|
|
723
|
+
* either form. Read when its meta loads; absent when it declares
|
|
724
|
+
* nothing, which leaves `uses` unchecked against a declaration.
|
|
725
|
+
*/
|
|
726
|
+
declaredUses?: string[];
|
|
727
|
+
/** The `{ alias: 'package@range' }` form of `pikku.uses`, per alias. */
|
|
728
|
+
dependencies?: AddonDependency[];
|
|
685
729
|
/**
|
|
686
730
|
* `true` offers every function the addon declared `mcp: true`; a list
|
|
687
731
|
* names the functions to offer, whatever the addon declared. Absent
|
|
@@ -713,6 +757,12 @@ export interface InspectorState {
|
|
|
713
757
|
scopes?: string[];
|
|
714
758
|
/** True when declared via `wireRemoteAddon` — import the addon's `.remote.gen` map, not `.internal.gen` */
|
|
715
759
|
remote?: boolean;
|
|
760
|
+
/**
|
|
761
|
+
* wireRemoteAddon: the semver range written after the package name
|
|
762
|
+
* (`@scope/name@^1.4.0`). Present means the contract comes from the
|
|
763
|
+
* SDK `pikku all` downloads, not from an installed package.
|
|
764
|
+
*/
|
|
765
|
+
range?: string;
|
|
716
766
|
/** wireRemoteAddon: whether an `auth` binding was supplied (vs a public surface) */
|
|
717
767
|
hasAuth?: boolean;
|
|
718
768
|
/** wireRemoteAddon: statically-known `auth.credentialId`, for verify */
|
|
@@ -878,6 +928,8 @@ export interface InspectorState {
|
|
|
878
928
|
openAPISpec: Record<string, any> | null;
|
|
879
929
|
diagnostics: InspectorDiagnostic[];
|
|
880
930
|
addonFunctions: Record<string, FunctionsMeta>;
|
|
931
|
+
/** The function metadata of each versioned `wireRemoteAddon`'s SDK, by namespace. Kept apart from `addonFunctions`: none of it runs here. */
|
|
932
|
+
remoteAddonFunctions?: Record<string, RemoteAddonFunctionsMeta>;
|
|
881
933
|
exportedContracts: InspectorExportedContractsState;
|
|
882
934
|
program: ts.Program | null;
|
|
883
935
|
}
|
|
@@ -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;
|