@pikku/inspector 0.12.42 → 0.12.43
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 +98 -0
- package/dist/add/add-addon-bans.js +1 -0
- package/dist/add/add-ai-agent.d.ts +3 -1
- package/dist/add/add-ai-agent.js +82 -0
- package/dist/add/add-credential.js +3 -1
- package/dist/add/add-functions.js +10 -9
- package/dist/add/add-gateway.js +3 -0
- package/dist/add/add-http-route.js +2 -2
- package/dist/add/add-mcp-prompt.js +0 -1
- package/dist/add/add-mcp-resource.js +0 -1
- package/dist/add/add-permission.d.ts +1 -1
- package/dist/add/add-permission.js +3 -177
- package/dist/add/add-queue-worker.js +3 -0
- package/dist/add/add-schedule.js +3 -0
- package/dist/add/add-scope.d.ts +2 -0
- package/dist/add/add-scope.js +146 -0
- package/dist/add/add-trigger.js +3 -0
- package/dist/add/add-wire-remote-addon.d.ts +10 -0
- package/dist/add/add-wire-remote-addon.js +66 -0
- package/dist/add/add-workflow-graph.js +32 -3
- package/dist/error-codes.d.ts +3 -0
- package/dist/error-codes.js +4 -0
- package/dist/inspector.js +11 -5
- package/dist/types.d.ts +19 -13
- package/dist/utils/custom-types-generator.js +2 -1
- package/dist/utils/ensure-function-metadata.d.ts +15 -0
- package/dist/utils/ensure-function-metadata.js +24 -0
- package/dist/utils/filter-inspector-state.js +9 -0
- package/dist/utils/get-property-value.d.ts +7 -2
- package/dist/utils/get-property-value.js +48 -1
- package/dist/utils/load-addon-functions-meta.js +81 -6
- package/dist/utils/permissions.d.ts +1 -21
- package/dist/utils/permissions.js +17 -108
- package/dist/utils/post-process.d.ts +30 -1
- package/dist/utils/post-process.js +150 -72
- package/dist/utils/serialize-inspector-state.d.ts +13 -8
- package/dist/utils/serialize-inspector-state.js +12 -6
- package/dist/utils/serialize-permissions-groups-meta.d.ts +0 -2
- package/dist/utils/serialize-permissions-groups-meta.js +0 -26
- package/dist/utils/workflow/derive-workflow-plan.js +9 -3
- package/dist/utils/workflow/dsl/extract-dsl-workflow.js +56 -1
- package/dist/utils/workflow/dsl/patterns.d.ts +4 -0
- package/dist/utils/workflow/dsl/patterns.js +11 -0
- package/dist/utils/workflow/graph/convert-dsl-to-graph.js +14 -0
- package/dist/utils/workflow/graph/finalize-workflows.d.ts +7 -0
- package/dist/utils/workflow/graph/finalize-workflows.js +11 -2
- package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +2 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.js +4 -0
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +6 -2
- package/dist/visit.js +4 -0
- package/package.json +3 -3
- package/src/add/add-addon-bans.ts +1 -0
- package/src/add/add-ai-agent.test.ts +87 -0
- package/src/add/add-ai-agent.ts +122 -0
- package/src/add/add-credential.ts +6 -0
- package/src/add/add-functions.ts +10 -12
- package/src/add/add-gateway.ts +11 -0
- package/src/add/add-http-route.ts +2 -8
- package/src/add/add-mcp-prompt.ts +0 -1
- package/src/add/add-mcp-resource.ts +0 -1
- package/src/add/add-permission.ts +4 -242
- package/src/add/add-queue-worker.ts +11 -0
- package/src/add/add-schedule.ts +11 -0
- package/src/add/add-scope.test.ts +346 -0
- package/src/add/add-scope.ts +225 -0
- package/src/add/add-trigger.ts +11 -0
- package/src/add/add-wire-remote-addon.ts +77 -0
- package/src/add/add-workflow-graph-input.test.ts +94 -0
- package/src/add/add-workflow-graph-notes.test.ts +74 -0
- package/src/add/add-workflow-graph.ts +35 -3
- package/src/add/inline-wiring-function.test.ts +104 -0
- package/src/add/validate-workflow-graph-addons.test.ts +102 -0
- package/src/error-codes.ts +5 -0
- package/src/inspector.ts +14 -4
- package/src/types.ts +16 -17
- package/src/utils/custom-types-generator.test.ts +26 -1
- package/src/utils/custom-types-generator.ts +2 -1
- package/src/utils/ensure-function-metadata.ts +42 -0
- package/src/utils/filter-inspector-state.test.ts +0 -2
- package/src/utils/filter-inspector-state.ts +9 -0
- package/src/utils/get-property-value.test.ts +141 -0
- package/src/utils/get-property-value.ts +62 -1
- package/src/utils/load-addon-functions-meta.test.ts +157 -0
- package/src/utils/load-addon-functions-meta.ts +94 -6
- package/src/utils/load-addon-scopes.test.ts +138 -0
- package/src/utils/permissions.test.ts +7 -232
- package/src/utils/permissions.ts +20 -160
- package/src/utils/post-process.test.ts +269 -4
- package/src/utils/post-process.ts +216 -95
- package/src/utils/serialize-inspector-state.ts +22 -25
- package/src/utils/serialize-permissions-groups-meta.ts +0 -28
- package/src/utils/workflow/derive-workflow-plan.test.ts +41 -3
- package/src/utils/workflow/derive-workflow-plan.ts +11 -4
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +66 -0
- package/src/utils/workflow/dsl/patterns.ts +18 -0
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +15 -0
- package/src/utils/workflow/graph/finalize-workflows.test.ts +50 -0
- package/src/utils/workflow/graph/finalize-workflows.ts +13 -2
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +6 -0
- package/src/utils/workflow/graph/workflow-graph.types.ts +8 -0
- package/src/visit.ts +4 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
import { getPropertyValue } from '../utils/get-property-value.js';
|
|
3
|
+
import { ErrorCode } from '../error-codes.js';
|
|
4
|
+
const SEPARATOR = ':';
|
|
5
|
+
const WILDCARD = '*';
|
|
6
|
+
/**
|
|
7
|
+
* Validates a single scope segment, reporting rather than throwing so the
|
|
8
|
+
* inspector can surface every problem in one pass.
|
|
9
|
+
*
|
|
10
|
+
* @returns true when the segment is usable.
|
|
11
|
+
*/
|
|
12
|
+
const isValidSegment = (segment, scopeName, logger) => {
|
|
13
|
+
if (segment.length === 0) {
|
|
14
|
+
logger.critical(ErrorCode.INVALID_VALUE, `Scope '${scopeName}' contains an empty segment.`);
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
if (segment.includes(SEPARATOR)) {
|
|
18
|
+
logger.critical(ErrorCode.INVALID_VALUE, `Scope segment '${segment}' in '${scopeName}' contains the '${SEPARATOR}' separator. ` +
|
|
19
|
+
`Nest scopes with the 'scopes' property instead of embedding '${SEPARATOR}' in a name.`);
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (segment === WILDCARD) {
|
|
23
|
+
logger.critical(ErrorCode.INVALID_VALUE, `Scope segment '${segment}' in '${scopeName}' is the wildcard. ` +
|
|
24
|
+
`'${WILDCARD}' is reserved for granting a scope and its descendants, and cannot be declared.`);
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Recursively extracts a `scopes: { ... }` object literal into nested metadata.
|
|
31
|
+
*/
|
|
32
|
+
const extractScopeNodes = (obj, scopeName, logger) => {
|
|
33
|
+
const nodes = {};
|
|
34
|
+
for (const prop of obj.properties) {
|
|
35
|
+
if (!ts.isPropertyAssignment(prop)) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
let segment;
|
|
39
|
+
if (ts.isIdentifier(prop.name)) {
|
|
40
|
+
segment = prop.name.text;
|
|
41
|
+
}
|
|
42
|
+
else if (ts.isStringLiteral(prop.name)) {
|
|
43
|
+
segment = prop.name.text;
|
|
44
|
+
}
|
|
45
|
+
if (segment === undefined) {
|
|
46
|
+
logger.critical(ErrorCode.NON_LITERAL_WIRE_NAME, `Scope '${scopeName}' has a nested scope whose key is not a literal.`);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (!isValidSegment(segment, scopeName, logger)) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (!ts.isObjectLiteralExpression(prop.initializer)) {
|
|
53
|
+
logger.critical(ErrorCode.INVALID_VALUE, `Nested scope '${segment}' in '${scopeName}' must be an object literal.`);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const node = {};
|
|
57
|
+
const displayName = getPropertyValue(prop.initializer, 'displayName');
|
|
58
|
+
if (displayName) {
|
|
59
|
+
node.displayName = displayName;
|
|
60
|
+
}
|
|
61
|
+
const description = getPropertyValue(prop.initializer, 'description');
|
|
62
|
+
if (description) {
|
|
63
|
+
node.description = description;
|
|
64
|
+
}
|
|
65
|
+
const nestedProp = prop.initializer.properties.find((p) => ts.isPropertyAssignment(p) &&
|
|
66
|
+
ts.isIdentifier(p.name) &&
|
|
67
|
+
p.name.text === 'scopes');
|
|
68
|
+
if (nestedProp &&
|
|
69
|
+
ts.isPropertyAssignment(nestedProp) &&
|
|
70
|
+
ts.isObjectLiteralExpression(nestedProp.initializer)) {
|
|
71
|
+
const nested = extractScopeNodes(nestedProp.initializer, scopeName, logger);
|
|
72
|
+
if (nested) {
|
|
73
|
+
node.scopes = nested;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
nodes[segment] = node;
|
|
77
|
+
}
|
|
78
|
+
return Object.keys(nodes).length > 0 ? nodes : undefined;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Unwraps `x as const` / `x as any` so a cast declaration is still extracted
|
|
82
|
+
* rather than silently skipped.
|
|
83
|
+
*/
|
|
84
|
+
const unwrapAs = (node) => ts.isAsExpression(node) || ts.isSatisfiesExpression(node)
|
|
85
|
+
? unwrapAs(node.expression)
|
|
86
|
+
: node;
|
|
87
|
+
export const addScope = (logger, node, checker, state, _options) => {
|
|
88
|
+
if (!ts.isCallExpression(node)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const expression = node.expression;
|
|
92
|
+
if (!ts.isIdentifier(expression) || expression.text !== 'wireScope') {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const firstArg = node.arguments[0];
|
|
96
|
+
if (!firstArg) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const unwrapped = unwrapAs(firstArg);
|
|
100
|
+
if (!ts.isObjectLiteralExpression(unwrapped)) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const sourceFile = node.getSourceFile().fileName;
|
|
104
|
+
// Roots are keyed exactly like the nodes beneath them, so each property of
|
|
105
|
+
// the call's single argument is one tree.
|
|
106
|
+
for (const prop of unwrapped.properties) {
|
|
107
|
+
if (!ts.isPropertyAssignment(prop)) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
let name;
|
|
111
|
+
if (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name)) {
|
|
112
|
+
name = prop.name.text;
|
|
113
|
+
}
|
|
114
|
+
if (name === undefined) {
|
|
115
|
+
logger.critical(ErrorCode.NON_LITERAL_WIRE_NAME, 'A scope is declared with a key that is not a literal.');
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (!isValidSegment(name, name, logger)) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (!ts.isObjectLiteralExpression(prop.initializer)) {
|
|
122
|
+
logger.critical(ErrorCode.INVALID_VALUE, `Scope '${name}' must be an object literal.`);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const root = prop.initializer;
|
|
126
|
+
const displayNameValue = getPropertyValue(root, 'displayName');
|
|
127
|
+
const descriptionValue = getPropertyValue(root, 'description');
|
|
128
|
+
let scopes;
|
|
129
|
+
const scopesProp = root.properties.find((p) => ts.isPropertyAssignment(p) &&
|
|
130
|
+
ts.isIdentifier(p.name) &&
|
|
131
|
+
p.name.text === 'scopes');
|
|
132
|
+
if (scopesProp &&
|
|
133
|
+
ts.isPropertyAssignment(scopesProp) &&
|
|
134
|
+
ts.isObjectLiteralExpression(scopesProp.initializer)) {
|
|
135
|
+
scopes = extractScopeNodes(scopesProp.initializer, name, logger);
|
|
136
|
+
}
|
|
137
|
+
state.scopes.files.add(sourceFile);
|
|
138
|
+
state.scopes.definitions.push({
|
|
139
|
+
name,
|
|
140
|
+
displayName: displayNameValue || undefined,
|
|
141
|
+
description: descriptionValue || undefined,
|
|
142
|
+
scopes,
|
|
143
|
+
sourceFile,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
};
|
package/dist/add/add-trigger.js
CHANGED
|
@@ -2,6 +2,7 @@ import * as ts from 'typescript';
|
|
|
2
2
|
import { getPropertyValue, getCommonWireMetaData, } from '../utils/get-property-value.js';
|
|
3
3
|
import { extractFunctionName, makeContextBasedId, } from '../utils/extract-function-name.js';
|
|
4
4
|
import { getPropertyAssignmentInitializer } from '../utils/type-utils.js';
|
|
5
|
+
import { ensureInlineWiringFunction } from '../utils/ensure-function-metadata.js';
|
|
5
6
|
import { resolveMiddleware } from '../utils/middleware.js';
|
|
6
7
|
import { extractWireNames } from '../utils/post-process.js';
|
|
7
8
|
import { resolveAddonName } from '../utils/resolve-addon-package.js';
|
|
@@ -45,6 +46,8 @@ const addWireTrigger = (logger, node, checker, state, firstArg) => {
|
|
|
45
46
|
if (!nameValue) {
|
|
46
47
|
return;
|
|
47
48
|
}
|
|
49
|
+
// Register metadata for a func inlined into the wiring (see helper).
|
|
50
|
+
ensureInlineWiringFunction(state, pikkuFuncId, nameValue, funcInitializer, checker, extracted.isHelper);
|
|
48
51
|
// --- resolve middleware ---
|
|
49
52
|
const middleware = resolveMiddleware(state, obj, tags, checker);
|
|
50
53
|
// --- track used functions/middleware for service aggregation ---
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
import type { InspectorState, InspectorLogger } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Detect wireRemoteAddon({ name: '...', package: '...' }) call expressions and
|
|
5
|
+
* record the namespace as a **remote** addon declaration. Only name + package
|
|
6
|
+
* are needed for codegen (serverUrl/auth are runtime closures resolved by the
|
|
7
|
+
* RPC runner); the `remote` marker tells the consumer's RPC-map generator to
|
|
8
|
+
* import the addon's `.remote.gen` map instead of `.internal.gen`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function addWireRemoteAddon(node: ts.Node, state: InspectorState, logger: InspectorLogger): void;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* Detect wireRemoteAddon({ name: '...', package: '...' }) call expressions and
|
|
4
|
+
* record the namespace as a **remote** addon declaration. Only name + package
|
|
5
|
+
* are needed for codegen (serverUrl/auth are runtime closures resolved by the
|
|
6
|
+
* RPC runner); the `remote` marker tells the consumer's RPC-map generator to
|
|
7
|
+
* import the addon's `.remote.gen` map instead of `.internal.gen`.
|
|
8
|
+
*/
|
|
9
|
+
export function addWireRemoteAddon(node, state, logger) {
|
|
10
|
+
if (!ts.isCallExpression(node))
|
|
11
|
+
return;
|
|
12
|
+
const { expression, arguments: args } = node;
|
|
13
|
+
if (!ts.isIdentifier(expression) || expression.text !== 'wireRemoteAddon')
|
|
14
|
+
return;
|
|
15
|
+
const [firstArg] = args;
|
|
16
|
+
if (!firstArg || !ts.isObjectLiteralExpression(firstArg))
|
|
17
|
+
return;
|
|
18
|
+
let name;
|
|
19
|
+
let pkg;
|
|
20
|
+
// The auth binding is a runtime closure; we only extract the statically
|
|
21
|
+
// knowable slot names so `pikku verify` can check they exist in the
|
|
22
|
+
// consumer's own wirings. `hasAuth` distinguishes "public" (omitted) from
|
|
23
|
+
// "bound via a custom resolve()" (present but no static id).
|
|
24
|
+
let hasAuth = false;
|
|
25
|
+
let authCredentialId;
|
|
26
|
+
let authSecretId;
|
|
27
|
+
for (const prop of firstArg.properties) {
|
|
28
|
+
if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name))
|
|
29
|
+
continue;
|
|
30
|
+
const key = prop.name.text;
|
|
31
|
+
if (key === 'name' && ts.isStringLiteral(prop.initializer)) {
|
|
32
|
+
name = prop.initializer.text;
|
|
33
|
+
}
|
|
34
|
+
else if (key === 'package' && ts.isStringLiteral(prop.initializer)) {
|
|
35
|
+
pkg = prop.initializer.text;
|
|
36
|
+
}
|
|
37
|
+
else if (key === 'auth' &&
|
|
38
|
+
ts.isObjectLiteralExpression(prop.initializer)) {
|
|
39
|
+
hasAuth = true;
|
|
40
|
+
for (const authProp of prop.initializer.properties) {
|
|
41
|
+
if (!ts.isPropertyAssignment(authProp) || !ts.isIdentifier(authProp.name))
|
|
42
|
+
continue;
|
|
43
|
+
if (authProp.name.text === 'credentialId' &&
|
|
44
|
+
ts.isStringLiteral(authProp.initializer)) {
|
|
45
|
+
authCredentialId = authProp.initializer.text;
|
|
46
|
+
}
|
|
47
|
+
else if (authProp.name.text === 'secretId' &&
|
|
48
|
+
ts.isStringLiteral(authProp.initializer)) {
|
|
49
|
+
authSecretId = authProp.initializer.text;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (!name || !pkg)
|
|
55
|
+
return;
|
|
56
|
+
logger.debug(`• Found wireRemoteAddon: ${name} → ${pkg} (remote)`);
|
|
57
|
+
state.rpc.wireAddonDeclarations.set(name, {
|
|
58
|
+
package: pkg,
|
|
59
|
+
remote: true,
|
|
60
|
+
hasAuth,
|
|
61
|
+
authCredentialId,
|
|
62
|
+
authSecretId,
|
|
63
|
+
});
|
|
64
|
+
state.rpc.usedAddons.add(name);
|
|
65
|
+
state.rpc.wireAddonFiles.add(node.getSourceFile().fileName);
|
|
66
|
+
}
|
|
@@ -2,6 +2,11 @@ import * as ts from 'typescript';
|
|
|
2
2
|
import { ErrorCode } from '../error-codes.js';
|
|
3
3
|
import { extractStringLiteral } from '../utils/extract-node-value.js';
|
|
4
4
|
function extractAstValue(expr, refParamName, templateParamName) {
|
|
5
|
+
// `'GET' as const` / parenthesised values wrap the real initializer — see
|
|
6
|
+
// through them or the whole property is silently dropped from the meta.
|
|
7
|
+
while (ts.isAsExpression(expr) || ts.isParenthesizedExpression(expr)) {
|
|
8
|
+
expr = expr.expression;
|
|
9
|
+
}
|
|
5
10
|
if (ts.isStringLiteral(expr)) {
|
|
6
11
|
return expr.text;
|
|
7
12
|
}
|
|
@@ -113,9 +118,13 @@ function extractInputMapping(node, _checker) {
|
|
|
113
118
|
const refParamName = node.parameters.length > 0 && ts.isIdentifier(node.parameters[0].name)
|
|
114
119
|
? node.parameters[0].name.text
|
|
115
120
|
: 'ref';
|
|
121
|
+
// The canonical form imports `template` from '@pikku/core/workflow' and calls
|
|
122
|
+
// it inside a single-param `(ref) => (...)` arrow; the older form passes it as
|
|
123
|
+
// a second arrow parameter. Support both — default to the imported name so a
|
|
124
|
+
// `template(...)` value is never dropped just because it isn't a 2nd param.
|
|
116
125
|
const templateParamName = node.parameters.length > 1 && ts.isIdentifier(node.parameters[1].name)
|
|
117
126
|
? node.parameters[1].name.text
|
|
118
|
-
:
|
|
127
|
+
: 'template';
|
|
119
128
|
const input = {};
|
|
120
129
|
for (const prop of bodyObj.properties) {
|
|
121
130
|
if (!ts.isPropertyAssignment(prop))
|
|
@@ -138,6 +147,12 @@ function extractInputMapping(node, _checker) {
|
|
|
138
147
|
* Extract next config (string, array, or record)
|
|
139
148
|
*/
|
|
140
149
|
function extractNextConfig(node, _checker) {
|
|
150
|
+
// Key-based branch `next` (`{ key: [targets] }`) is authored with an `as any`
|
|
151
|
+
// cast because the graph's NextConfig can't narrow record targets to node-id
|
|
152
|
+
// literals; see through that (and parens) so the routing survives into the meta.
|
|
153
|
+
while (ts.isAsExpression(node) || ts.isParenthesizedExpression(node)) {
|
|
154
|
+
node = node.expression;
|
|
155
|
+
}
|
|
141
156
|
if (ts.isStringLiteral(node)) {
|
|
142
157
|
return node.text;
|
|
143
158
|
}
|
|
@@ -207,6 +222,7 @@ function extractWorkflowGraphConfig(configArg, checker) {
|
|
|
207
222
|
let name;
|
|
208
223
|
let description;
|
|
209
224
|
let tags;
|
|
225
|
+
let notes;
|
|
210
226
|
let disabled;
|
|
211
227
|
let nodesNode;
|
|
212
228
|
let configNode;
|
|
@@ -231,6 +247,12 @@ function extractWorkflowGraphConfig(configArg, checker) {
|
|
|
231
247
|
.filter(ts.isStringLiteral)
|
|
232
248
|
.map((el) => el.text);
|
|
233
249
|
}
|
|
250
|
+
else if (propName === 'notes' &&
|
|
251
|
+
ts.isArrayLiteralExpression(prop.initializer)) {
|
|
252
|
+
notes = prop.initializer.elements
|
|
253
|
+
.filter(ts.isStringLiteral)
|
|
254
|
+
.map((el) => el.text);
|
|
255
|
+
}
|
|
234
256
|
else if (propName === 'nodes' &&
|
|
235
257
|
ts.isObjectLiteralExpression(prop.initializer)) {
|
|
236
258
|
nodesNode = prop.initializer;
|
|
@@ -240,7 +262,7 @@ function extractWorkflowGraphConfig(configArg, checker) {
|
|
|
240
262
|
configNode = prop.initializer;
|
|
241
263
|
}
|
|
242
264
|
}
|
|
243
|
-
return { name, description, tags, disabled, nodesNode, configNode };
|
|
265
|
+
return { name, description, tags, notes, disabled, nodesNode, configNode };
|
|
244
266
|
}
|
|
245
267
|
/**
|
|
246
268
|
* Extract graph nodes from the new pikkuWorkflowGraph format
|
|
@@ -283,6 +305,7 @@ function extractGraphFromNewFormat(nodesNode, configNode, checker, state) {
|
|
|
283
305
|
onError: undefined,
|
|
284
306
|
retries: undefined,
|
|
285
307
|
retryDelay: undefined,
|
|
308
|
+
notes: undefined,
|
|
286
309
|
};
|
|
287
310
|
}
|
|
288
311
|
// Extract config for each node from 'config' property
|
|
@@ -305,6 +328,7 @@ function extractGraphFromNewFormat(nodesNode, configNode, checker, state) {
|
|
|
305
328
|
nodes[nodeId].input = nodeConfig.input;
|
|
306
329
|
nodes[nodeId].retries = nodeConfig.retries;
|
|
307
330
|
nodes[nodeId].retryDelay = nodeConfig.retryDelay;
|
|
331
|
+
nodes[nodeId].notes = nodeConfig.notes;
|
|
308
332
|
}
|
|
309
333
|
}
|
|
310
334
|
}
|
|
@@ -320,6 +344,7 @@ function extractNodeConfigFromObject(obj, checker) {
|
|
|
320
344
|
let input = {};
|
|
321
345
|
let retries = undefined;
|
|
322
346
|
let retryDelay = undefined;
|
|
347
|
+
let notes = undefined;
|
|
323
348
|
for (const prop of obj.properties) {
|
|
324
349
|
if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name))
|
|
325
350
|
continue;
|
|
@@ -346,8 +371,11 @@ function extractNodeConfigFromObject(obj, checker) {
|
|
|
346
371
|
retryDelay = prop.initializer.text;
|
|
347
372
|
}
|
|
348
373
|
}
|
|
374
|
+
else if (propName === 'notes') {
|
|
375
|
+
notes = extractStringLiteral(prop.initializer, checker);
|
|
376
|
+
}
|
|
349
377
|
}
|
|
350
|
-
return { next, onError, input, retries, retryDelay };
|
|
378
|
+
return { next, onError, input, retries, retryDelay, notes };
|
|
351
379
|
}
|
|
352
380
|
/**
|
|
353
381
|
* Inspector for pikkuWorkflowGraph() calls
|
|
@@ -402,6 +430,7 @@ export const addWorkflowGraph = (logger, node, checker, state) => {
|
|
|
402
430
|
source: 'graph',
|
|
403
431
|
description: graphConfig.description,
|
|
404
432
|
tags: graphConfig.tags,
|
|
433
|
+
notes: graphConfig.notes,
|
|
405
434
|
nodes: graphNodes,
|
|
406
435
|
entryNodeIds,
|
|
407
436
|
};
|
package/dist/error-codes.d.ts
CHANGED
|
@@ -22,6 +22,9 @@ export declare enum ErrorCode {
|
|
|
22
22
|
EXPECT_EVENTUALLY_SCENARIO_ONLY = "PKU675",
|
|
23
23
|
WORKFLOW_ORCHESTRATOR_NOT_CONFIGURED = "PKU600",
|
|
24
24
|
INVALID_DSL_WORKFLOW = "PKU641",
|
|
25
|
+
WORKFLOW_GRAPH_ADDON_NOT_WIRED = "PKU642",
|
|
26
|
+
REMOTE_ADDON_NOT_DEV_DEPENDENCY = "PKU338",
|
|
27
|
+
REMOTE_ADDON_AUTH_UNRESOLVED = "PKU339",
|
|
25
28
|
DB_COLUMN_NAME_TYPE_CONTRADICTION = "PKU480",
|
|
26
29
|
DB_JSON_COLUMN_UNTYPED = "PKU481",
|
|
27
30
|
DB_FORMAT_ON_NON_STRING = "PKU482",
|
package/dist/error-codes.js
CHANGED
|
@@ -24,6 +24,10 @@ export var ErrorCode;
|
|
|
24
24
|
ErrorCode["EXPECT_EVENTUALLY_SCENARIO_ONLY"] = "PKU675";
|
|
25
25
|
ErrorCode["WORKFLOW_ORCHESTRATOR_NOT_CONFIGURED"] = "PKU600";
|
|
26
26
|
ErrorCode["INVALID_DSL_WORKFLOW"] = "PKU641";
|
|
27
|
+
ErrorCode["WORKFLOW_GRAPH_ADDON_NOT_WIRED"] = "PKU642";
|
|
28
|
+
// Remote addon (wireRemoteAddon) validation
|
|
29
|
+
ErrorCode["REMOTE_ADDON_NOT_DEV_DEPENDENCY"] = "PKU338";
|
|
30
|
+
ErrorCode["REMOTE_ADDON_AUTH_UNRESOLVED"] = "PKU339";
|
|
27
31
|
// Database schema codegen warnings
|
|
28
32
|
ErrorCode["DB_COLUMN_NAME_TYPE_CONTRADICTION"] = "PKU480";
|
|
29
33
|
ErrorCode["DB_JSON_COLUMN_UNTYPED"] = "PKU481";
|
package/dist/inspector.js
CHANGED
|
@@ -6,7 +6,7 @@ import { TypesMap } from './types-map.js';
|
|
|
6
6
|
import { getFilesAndMethods } from './utils/get-files-and-methods.js';
|
|
7
7
|
import { findCommonAncestor } from './utils/find-root-dir.js';
|
|
8
8
|
import { createNestedProjectFilter } from './utils/nested-project-filter.js';
|
|
9
|
-
import { aggregateRequiredServices, stampAuthHandlerServices, validateAgentModels, validateSecretOverrides, validateVariableOverrides, validateCredentialOverrides, computeResolvedIOTypes, computeMiddlewareGroupsMeta, computePermissionsGroupsMeta, computeRequiredSchemas, computeDiagnostics, validateSchemaWiringSeparation, } from './utils/post-process.js';
|
|
9
|
+
import { aggregateRequiredServices, stampAuthHandlerServices, validateAgentModels, validateSecretOverrides, validateVariableOverrides, validateCredentialOverrides, validateRemoteAddonDependencies, validateRemoteAddonAuth, validateScopeReferences, computeResolvedIOTypes, computeMiddlewareGroupsMeta, computePermissionsGroupsMeta, computeRequiredSchemas, computeDiagnostics, validateSchemaWiringSeparation, validateWorkflowGraphAddons, } from './utils/post-process.js';
|
|
10
10
|
import { generateOpenAPISpec } from './utils/serialize-openapi-json.js';
|
|
11
11
|
import { pikkuState } from '@pikku/core/internal';
|
|
12
12
|
import { resolveLatestVersions } from './utils/resolve-versions.js';
|
|
@@ -59,7 +59,6 @@ export function getInitialInspectorState(rootDir) {
|
|
|
59
59
|
},
|
|
60
60
|
files: new Set(),
|
|
61
61
|
routeMiddleware: new Map(),
|
|
62
|
-
routePermissions: new Map(),
|
|
63
62
|
},
|
|
64
63
|
channels: {
|
|
65
64
|
files: new Set(),
|
|
@@ -94,6 +93,8 @@ export function getInitialInspectorState(rootDir) {
|
|
|
94
93
|
internalFiles: new Map(),
|
|
95
94
|
exposedMeta: {},
|
|
96
95
|
exposedFiles: new Map(),
|
|
96
|
+
remoteMeta: {},
|
|
97
|
+
remoteFiles: new Map(),
|
|
97
98
|
invokedFunctions: new Set(),
|
|
98
99
|
invokedFunctionsByFile: new Map(),
|
|
99
100
|
usedAddons: new Set(),
|
|
@@ -135,6 +136,10 @@ export function getInitialInspectorState(rootDir) {
|
|
|
135
136
|
definitions: [],
|
|
136
137
|
files: new Set(),
|
|
137
138
|
},
|
|
139
|
+
scopes: {
|
|
140
|
+
definitions: [],
|
|
141
|
+
files: new Set(),
|
|
142
|
+
},
|
|
138
143
|
variables: {
|
|
139
144
|
definitions: [],
|
|
140
145
|
files: new Set(),
|
|
@@ -160,7 +165,6 @@ export function getInitialInspectorState(rootDir) {
|
|
|
160
165
|
permissions: {
|
|
161
166
|
definitions: {},
|
|
162
167
|
instances: {},
|
|
163
|
-
tagPermissions: new Map(),
|
|
164
168
|
},
|
|
165
169
|
serviceAggregation: {
|
|
166
170
|
requiredServices: new Set(),
|
|
@@ -184,8 +188,6 @@ export function getInitialInspectorState(rootDir) {
|
|
|
184
188
|
},
|
|
185
189
|
permissionsGroupsMeta: {
|
|
186
190
|
definitions: {},
|
|
187
|
-
httpGroups: {},
|
|
188
|
-
tagGroups: {},
|
|
189
191
|
},
|
|
190
192
|
requiredSchemas: new Set(),
|
|
191
193
|
openAPISpec: null,
|
|
@@ -307,6 +309,7 @@ export const inspect = async (logger, routeFiles, options = {}) => {
|
|
|
307
309
|
computePermissionsGroupsMeta(state);
|
|
308
310
|
computeDiagnostics(state);
|
|
309
311
|
validateSchemaWiringSeparation(logger, state);
|
|
312
|
+
validateWorkflowGraphAddons(logger, state);
|
|
310
313
|
if (options.openAPI) {
|
|
311
314
|
state.openAPISpec = await generateOpenAPISpec(logger, state.functions.meta, state.http.meta, state.schemas, options.openAPI.additionalInfo, pikkuState(null, 'misc', 'errors'));
|
|
312
315
|
}
|
|
@@ -314,6 +317,9 @@ export const inspect = async (logger, routeFiles, options = {}) => {
|
|
|
314
317
|
validateSecretOverrides(logger, state);
|
|
315
318
|
validateVariableOverrides(logger, state);
|
|
316
319
|
validateCredentialOverrides(logger, state);
|
|
320
|
+
validateRemoteAddonDependencies(logger, state);
|
|
321
|
+
validateRemoteAddonAuth(logger, state);
|
|
322
|
+
validateScopeReferences(logger, state);
|
|
317
323
|
}
|
|
318
324
|
state.program = program;
|
|
319
325
|
return state;
|
package/dist/types.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ import type { CLICommandMeta } from '@pikku/core/cli';
|
|
|
13
13
|
import type { NodesMeta } from '@pikku/core/node';
|
|
14
14
|
import type { SecretDefinitions } from '@pikku/core/secret';
|
|
15
15
|
import type { CredentialDefinitions } from '@pikku/core/credential';
|
|
16
|
+
import type { ScopeDefinitions } from '@pikku/core/scope';
|
|
16
17
|
import type { VariableDefinitions } from '@pikku/core/variable';
|
|
17
18
|
import type { TypesMap } from './types-map.js';
|
|
18
19
|
import type { FunctionsMeta, FunctionServicesMeta, FunctionWiresMeta, JSONValue } from '@pikku/core';
|
|
@@ -39,21 +40,11 @@ export interface MiddlewareGroupMeta {
|
|
|
39
40
|
instanceIds: string[];
|
|
40
41
|
isFactory: boolean;
|
|
41
42
|
}
|
|
42
|
-
export interface PermissionGroupMeta {
|
|
43
|
-
exportName: string | null;
|
|
44
|
-
sourceFile: string;
|
|
45
|
-
position: number;
|
|
46
|
-
services: FunctionServicesMeta;
|
|
47
|
-
count: number;
|
|
48
|
-
instanceIds: string[];
|
|
49
|
-
isFactory: boolean;
|
|
50
|
-
}
|
|
51
43
|
export interface InspectorHTTPState {
|
|
52
44
|
metaInputTypes: MetaInputTypes;
|
|
53
45
|
meta: HTTPWiringsMeta;
|
|
54
46
|
files: Set<string>;
|
|
55
47
|
routeMiddleware: Map<string, MiddlewareGroupMeta>;
|
|
56
|
-
routePermissions: Map<string, PermissionGroupMeta>;
|
|
57
48
|
}
|
|
58
49
|
/**
|
|
59
50
|
* Schema vendor types for Standard Schema compliant validators
|
|
@@ -184,7 +175,6 @@ export interface InspectorPermissionInstance {
|
|
|
184
175
|
export interface InspectorPermissionState {
|
|
185
176
|
definitions: Record<string, InspectorPermissionDefinition>;
|
|
186
177
|
instances: Record<string, InspectorPermissionInstance>;
|
|
187
|
-
tagPermissions: Map<string, PermissionGroupMeta>;
|
|
188
178
|
}
|
|
189
179
|
export type InspectorFilters = {
|
|
190
180
|
names?: string[];
|
|
@@ -418,6 +408,12 @@ export interface InspectorState {
|
|
|
418
408
|
path: string;
|
|
419
409
|
exportedName: string;
|
|
420
410
|
}>;
|
|
411
|
+
/** Functions marked `remote: true` — the surface a wireRemoteAddon consumer imports */
|
|
412
|
+
remoteMeta: Record<string, string>;
|
|
413
|
+
remoteFiles: Map<string, {
|
|
414
|
+
path: string;
|
|
415
|
+
exportedName: string;
|
|
416
|
+
}>;
|
|
421
417
|
invokedFunctions: Set<string>;
|
|
422
418
|
invokedFunctionsByFile: Map<string, Set<string>>;
|
|
423
419
|
usedAddons: Set<string>;
|
|
@@ -425,6 +421,14 @@ export interface InspectorState {
|
|
|
425
421
|
package: string;
|
|
426
422
|
rpcEndpoint?: string;
|
|
427
423
|
mcp?: boolean;
|
|
424
|
+
/** True when declared via `wireRemoteAddon` — import the addon's `.remote.gen` map, not `.internal.gen` */
|
|
425
|
+
remote?: boolean;
|
|
426
|
+
/** wireRemoteAddon: whether an `auth` binding was supplied (vs a public surface) */
|
|
427
|
+
hasAuth?: boolean;
|
|
428
|
+
/** wireRemoteAddon: statically-known `auth.credentialId`, for verify */
|
|
429
|
+
authCredentialId?: string;
|
|
430
|
+
/** wireRemoteAddon: statically-known `auth.secretId`, for verify */
|
|
431
|
+
authSecretId?: string;
|
|
428
432
|
secretOverrides?: Record<string, string>;
|
|
429
433
|
variableOverrides?: Record<string, string>;
|
|
430
434
|
credentialOverrides?: Record<string, string>;
|
|
@@ -480,6 +484,10 @@ export interface InspectorState {
|
|
|
480
484
|
definitions: CredentialDefinitions;
|
|
481
485
|
files: Set<string>;
|
|
482
486
|
};
|
|
487
|
+
scopes: {
|
|
488
|
+
definitions: ScopeDefinitions;
|
|
489
|
+
files: Set<string>;
|
|
490
|
+
};
|
|
483
491
|
variables: {
|
|
484
492
|
definitions: VariableDefinitions;
|
|
485
493
|
files: Set<string>;
|
|
@@ -518,8 +526,6 @@ export interface InspectorState {
|
|
|
518
526
|
};
|
|
519
527
|
permissionsGroupsMeta: {
|
|
520
528
|
definitions: Record<string, InspectorPermissionDefinition>;
|
|
521
|
-
httpGroups: Record<string, PermissionGroupMeta>;
|
|
522
|
-
tagGroups: Record<string, PermissionGroupMeta>;
|
|
523
529
|
};
|
|
524
530
|
requiredSchemas: Set<string>;
|
|
525
531
|
openAPISpec: Record<string, any> | null;
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* ts-json-schema-generator can discover inline/custom types from typesMap.
|
|
6
6
|
*/
|
|
7
7
|
export function sanitizeTypeName(name) {
|
|
8
|
-
|
|
8
|
+
const sanitized = name.replace(/[^a-zA-Z0-9_$]/g, '_');
|
|
9
|
+
return /^[0-9]/.test(sanitized) ? `_${sanitized}` : sanitized;
|
|
9
10
|
}
|
|
10
11
|
const CLASSIFICATION_WRAPPERS = new Set(['Private', 'Pii', 'Secret']);
|
|
11
12
|
function findMatchingAngleBracket(type, startIndex) {
|
|
@@ -7,3 +7,18 @@ import type { InspectorState } from '../types.js';
|
|
|
7
7
|
* extracts tags/middleware/permissions from the pikkuFunc() config.
|
|
8
8
|
*/
|
|
9
9
|
export declare function ensureFunctionMetadata(state: InspectorState, pikkuFuncId: string, fallbackName?: string, funcInitializer?: ts.Node, checker?: ts.TypeChecker, isHelper?: boolean): void;
|
|
10
|
+
/**
|
|
11
|
+
* Register metadata for a `func:` inlined into a wiring (queue/scheduler/
|
|
12
|
+
* trigger/gateway/…).
|
|
13
|
+
*
|
|
14
|
+
* An inline function has no exported name, so `addFunctions` skips it (it bails
|
|
15
|
+
* on `__temp_` ids) and only the wiring's context-based id exists. Without this
|
|
16
|
+
* the wiring points at a function that was never registered and the worker
|
|
17
|
+
* resolves to nothing at runtime ("Missing generated metadata for ...").
|
|
18
|
+
*
|
|
19
|
+
* The stub also carries no session info, so the runtime would treat it as
|
|
20
|
+
* session-required and every invocation would fail with "Authentication
|
|
21
|
+
* required". Derive `sessionless` from the helper the function was built with —
|
|
22
|
+
* the same rule `addFunctions` applies to named ones (`!== 'pikkuFunc'`).
|
|
23
|
+
*/
|
|
24
|
+
export declare function ensureInlineWiringFunction(state: InspectorState, pikkuFuncId: string, fallbackName: string | undefined, funcInitializer: ts.Node, checker: ts.TypeChecker, isHelper?: boolean): void;
|
|
@@ -207,6 +207,30 @@ export function ensureFunctionMetadata(state, pikkuFuncId, fallbackName, funcIni
|
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Register metadata for a `func:` inlined into a wiring (queue/scheduler/
|
|
212
|
+
* trigger/gateway/…).
|
|
213
|
+
*
|
|
214
|
+
* An inline function has no exported name, so `addFunctions` skips it (it bails
|
|
215
|
+
* on `__temp_` ids) and only the wiring's context-based id exists. Without this
|
|
216
|
+
* the wiring points at a function that was never registered and the worker
|
|
217
|
+
* resolves to nothing at runtime ("Missing generated metadata for ...").
|
|
218
|
+
*
|
|
219
|
+
* The stub also carries no session info, so the runtime would treat it as
|
|
220
|
+
* session-required and every invocation would fail with "Authentication
|
|
221
|
+
* required". Derive `sessionless` from the helper the function was built with —
|
|
222
|
+
* the same rule `addFunctions` applies to named ones (`!== 'pikkuFunc'`).
|
|
223
|
+
*/
|
|
224
|
+
export function ensureInlineWiringFunction(state, pikkuFuncId, fallbackName, funcInitializer, checker, isHelper) {
|
|
225
|
+
ensureFunctionMetadata(state, pikkuFuncId, fallbackName, funcInitializer, checker, isHelper);
|
|
226
|
+
const meta = state.functions.meta[pikkuFuncId];
|
|
227
|
+
if (meta &&
|
|
228
|
+
meta.sessionless === undefined &&
|
|
229
|
+
ts.isCallExpression(funcInitializer) &&
|
|
230
|
+
ts.isIdentifier(funcInitializer.expression)) {
|
|
231
|
+
meta.sessionless = funcInitializer.expression.text !== 'pikkuFunc';
|
|
232
|
+
}
|
|
233
|
+
}
|
|
210
234
|
function populateTypesLookup(state, pikkuFuncId, funcInitializer, checker) {
|
|
211
235
|
const typeArgs = funcInitializer.typeArguments;
|
|
212
236
|
if (typeArgs && typeArgs.length >= 1) {
|
|
@@ -334,6 +334,8 @@ export function filterInspectorState(state, filters, logger) {
|
|
|
334
334
|
internalFiles: new Map(state.rpc.internalFiles),
|
|
335
335
|
exposedMeta: { ...state.rpc.exposedMeta },
|
|
336
336
|
exposedFiles: new Map(state.rpc.exposedFiles),
|
|
337
|
+
remoteMeta: { ...state.rpc.remoteMeta },
|
|
338
|
+
remoteFiles: new Map(state.rpc.remoteFiles),
|
|
337
339
|
invokedFunctions: new Set(state.rpc.invokedFunctions),
|
|
338
340
|
},
|
|
339
341
|
cli: {
|
|
@@ -746,6 +748,13 @@ export function filterInspectorState(state, filters, logger) {
|
|
|
746
748
|
filteredState.rpc.exposedFiles.delete(key);
|
|
747
749
|
}
|
|
748
750
|
}
|
|
751
|
+
for (const key of Object.keys(filteredState.rpc.remoteMeta)) {
|
|
752
|
+
const targetFuncId = filteredState.rpc.remoteMeta[key];
|
|
753
|
+
if (!survivingFuncIds.has(targetFuncId) && !survivingFuncIds.has(key)) {
|
|
754
|
+
delete filteredState.rpc.remoteMeta[key];
|
|
755
|
+
filteredState.rpc.remoteFiles.delete(key);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
749
758
|
// Prune invokedFunctions to match surviving functions
|
|
750
759
|
for (const funcId of filteredState.rpc.invokedFunctions) {
|
|
751
760
|
if (!survivingFuncIds.has(funcId)) {
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
2
|
import { ErrorCode } from '../error-codes.js';
|
|
3
|
+
export declare const getArrayPropertyValue: (obj: ts.ObjectLiteralExpression, propertyName: string) => string[] | null;
|
|
3
4
|
/**
|
|
4
|
-
* Extracts
|
|
5
|
+
* Extracts a string->string record from an object property.
|
|
6
|
+
*
|
|
7
|
+
* `getPropertyValue` falls through to `getText()` for an object literal, which
|
|
8
|
+
* returns the raw source text rather than the record — so callers that need the
|
|
9
|
+
* entries (e.g. oauth2 `additionalParams`) must use this instead.
|
|
5
10
|
*/
|
|
6
|
-
export declare const
|
|
11
|
+
export declare const getRecordPropertyValue: (obj: ts.ObjectLiteralExpression, propertyName: string) => Record<string, string> | null;
|
|
7
12
|
/**
|
|
8
13
|
* Wiring identity fields (`name`, `secretId`, `variableId`, …) are read
|
|
9
14
|
* STATICALLY from source — a const or variable reference is keyed by its
|