@pikku/inspector 0.12.42 → 0.12.44
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 +266 -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 +5 -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-keyed-wiring.js +2 -0
- 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/add/add-workflow.js +9 -4
- 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/schema-generator.js +19 -4
- 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/deserialize-dsl-workflow.js +373 -91
- package/dist/utils/workflow/dsl/extract-dsl-workflow.js +274 -100
- 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 +76 -5
- 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 +8 -4
- package/dist/visit.js +4 -0
- package/package.json +3 -3
- package/run-tests.sh +1 -1
- 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 +8 -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-keyed-wiring.ts +2 -0
- 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-edge-cases.test.ts +201 -0
- package/src/add/add-workflow-fanout.test.ts +60 -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/add-workflow-on-error.test.ts +118 -0
- package/src/add/add-workflow-return-switch.test.ts +121 -0
- package/src/add/add-workflow-silent-drops.test.ts +163 -0
- package/src/add/add-workflow-sleep-expression.test.ts +97 -0
- package/src/add/add-workflow.ts +9 -4
- package/src/add/config-value-metadata.test.ts +169 -0
- 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/schema-generator.test.ts +164 -0
- package/src/utils/schema-generator.ts +26 -3
- 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/deserialize-dsl-workflow.ts +480 -119
- package/src/utils/workflow/dsl/deserialize-fanout-body.test.ts +71 -0
- package/src/utils/workflow/dsl/deserialize-fanout-fidelity.test.ts +99 -0
- package/src/utils/workflow/dsl/deserialize-graph-next.test.ts +161 -0
- package/src/utils/workflow/dsl/deserialize-roundtrip-bindings.test.ts +121 -0
- package/src/utils/workflow/dsl/deserialize-roundtrip-values.test.ts +116 -0
- package/src/utils/workflow/dsl/deserialize-roundtrip.test.ts +145 -0
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +311 -105
- package/src/utils/workflow/dsl/patterns.ts +18 -0
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +82 -10
- package/src/utils/workflow/graph/fanout-node-identity.test.ts +105 -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 +11 -2
- package/src/visit.ts +4 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -19,7 +19,7 @@ export declare function stampAuthHandlerServices(state: InspectorState | Omit<In
|
|
|
19
19
|
* Only extracts type:'wire' variants (individual middleware/permissions).
|
|
20
20
|
* Skips type:'http' and type:'tag' (reference groups, not individuals).
|
|
21
21
|
*/
|
|
22
|
-
export declare function extractWireNames(list?: MiddlewareMetadata
|
|
22
|
+
export declare function extractWireNames(list?: Array<MiddlewareMetadata | PermissionMetadata>): string[];
|
|
23
23
|
/**
|
|
24
24
|
* Aggregates all required services from wired functions, middleware, and permissions.
|
|
25
25
|
* Must be called after AST traversal completes.
|
|
@@ -31,11 +31,32 @@ export declare function aggregateRequiredServices(state: InspectorState | Omit<I
|
|
|
31
31
|
export declare function validateSecretOverrides(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
|
|
32
32
|
export declare function validateCredentialOverrides(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
|
|
33
33
|
export declare function validateVariableOverrides(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
|
|
34
|
+
/**
|
|
35
|
+
* A `wireRemoteAddon` package ships types only — its handlers run on the host —
|
|
36
|
+
* so it MUST be a devDependency, not a production dependency (a prod dep would
|
|
37
|
+
* drag in the runtime deps remote consumption exists to avoid). This is the
|
|
38
|
+
* mirror image of `wireAddon`, which requires a production dependency.
|
|
39
|
+
*/
|
|
40
|
+
export declare function validateRemoteAddonDependencies(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
|
|
41
|
+
/**
|
|
42
|
+
* The auth a `wireRemoteAddon` consumer binds must reference a slot the consumer
|
|
43
|
+
* actually declares: a `credentialId` must be a wired credential, a `secretId`
|
|
44
|
+
* a wired secret. A custom `resolve()` and a public (omitted) surface are not
|
|
45
|
+
* statically checkable and are left to runtime.
|
|
46
|
+
*/
|
|
47
|
+
export declare function validateRemoteAddonAuth(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
|
|
34
48
|
export declare function computeResolvedIOTypes(state: InspectorState): void;
|
|
35
49
|
export declare function computeMiddlewareGroupsMeta(state: InspectorState): void;
|
|
36
50
|
export declare function computePermissionsGroupsMeta(state: InspectorState): void;
|
|
37
51
|
export declare function computeRequiredSchemas(state: InspectorState, options: InspectorOptions): void;
|
|
38
52
|
export declare function validateAgentModels(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
|
|
53
|
+
/**
|
|
54
|
+
* A `pikkuWorkflowGraph` node that references the `graph:` namespace (e.g.
|
|
55
|
+
* `graph:editFields`) needs @pikku/addon-graph wired — otherwise the RPC never
|
|
56
|
+
* registers and codegen fails deep in type-checking with an opaque error. Fail
|
|
57
|
+
* early with an actionable message pointing at `scaffold: { graph: true }`.
|
|
58
|
+
*/
|
|
59
|
+
export declare function validateWorkflowGraphAddons(logger: InspectorLogger, state: InspectorState): void;
|
|
39
60
|
/**
|
|
40
61
|
* Validates that Zod schemas and wiring side-effects (wireHTTPRoutes,
|
|
41
62
|
* addPermission, addHTTPMiddleware, etc.) do not coexist in the same file.
|
|
@@ -46,3 +67,11 @@ export declare function validateAgentModels(logger: InspectorLogger, state: Insp
|
|
|
46
67
|
*/
|
|
47
68
|
export declare function validateSchemaWiringSeparation(logger: InspectorLogger, state: InspectorState): void;
|
|
48
69
|
export declare function computeDiagnostics(state: InspectorState): void;
|
|
70
|
+
/**
|
|
71
|
+
* Validates that every scope referenced by a function is declared via
|
|
72
|
+
* `wireScope`. Runs after all visitors, so declaration order does not matter.
|
|
73
|
+
*
|
|
74
|
+
* A `*` suffix is a wildcard requirement: `admin:*` requires the `admin` scope
|
|
75
|
+
* to be declared, and grants its whole subtree.
|
|
76
|
+
*/
|
|
77
|
+
export declare function validateScopeReferences(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
1
3
|
import { extractTypeKeys } from './type-utils.js';
|
|
2
4
|
import { ErrorCode } from '../error-codes.js';
|
|
3
5
|
import { AUTH_HANDLER_FUNC_ID } from '../add/add-auth.js';
|
|
6
|
+
import { flattenScopeDefinitions } from '@pikku/core/scope';
|
|
4
7
|
/**
|
|
5
8
|
* Stamp the inspected authorize/callbacks service set onto the generated auth
|
|
6
9
|
* handler's function meta.
|
|
@@ -39,28 +42,22 @@ export function extractWireNames(list) {
|
|
|
39
42
|
.map((item) => item.name);
|
|
40
43
|
}
|
|
41
44
|
/**
|
|
42
|
-
* Helper to expand middleware
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
+
* Helper to expand middleware groups into individual names and add their
|
|
46
|
+
* services to the aggregation. Handles tag-based and HTTP-pattern-based
|
|
47
|
+
* middleware groups. Permissions are function-scoped only and carry no groups.
|
|
45
48
|
*/
|
|
46
|
-
function expandAndAddGroupServices(list, state, addServices
|
|
49
|
+
function expandAndAddGroupServices(list, state, addServices) {
|
|
47
50
|
if (!list)
|
|
48
51
|
return;
|
|
49
52
|
for (const item of list) {
|
|
50
53
|
if (item.type === 'tag') {
|
|
51
|
-
|
|
52
|
-
const groupMeta = isMiddleware
|
|
53
|
-
? state.middleware.tagMiddleware.get(item.tag)
|
|
54
|
-
: state.permissions.tagPermissions.get(item.tag);
|
|
54
|
+
const groupMeta = state.middleware.tagMiddleware.get(item.tag);
|
|
55
55
|
if (groupMeta?.services) {
|
|
56
56
|
addServices(groupMeta.services);
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
else if (item.type === 'http' && 'route' in item) {
|
|
60
|
-
|
|
61
|
-
const groupMeta = isMiddleware
|
|
62
|
-
? state.http.routeMiddleware.get(item.route)
|
|
63
|
-
: state.http.routePermissions.get(item.route);
|
|
60
|
+
const groupMeta = state.http.routeMiddleware.get(item.route);
|
|
64
61
|
if (groupMeta?.services) {
|
|
65
62
|
addServices(groupMeta.services);
|
|
66
63
|
}
|
|
@@ -149,34 +146,27 @@ export function aggregateRequiredServices(state) {
|
|
|
149
146
|
'options',
|
|
150
147
|
]) {
|
|
151
148
|
for (const routeMeta of Object.values(state.http.meta[method])) {
|
|
152
|
-
expandAndAddGroupServices(routeMeta.middleware, state, addServices
|
|
153
|
-
expandAndAddGroupServices(routeMeta.permissions, state, addServices, false);
|
|
149
|
+
expandAndAddGroupServices(routeMeta.middleware, state, addServices);
|
|
154
150
|
}
|
|
155
151
|
}
|
|
156
152
|
// Also check other wiring types (channels, queues, schedulers, MCP)
|
|
157
153
|
for (const channelMeta of Object.values(state.channels.meta)) {
|
|
158
|
-
expandAndAddGroupServices(channelMeta.middleware, state, addServices
|
|
159
|
-
expandAndAddGroupServices(channelMeta.permissions, state, addServices, false);
|
|
154
|
+
expandAndAddGroupServices(channelMeta.middleware, state, addServices);
|
|
160
155
|
}
|
|
161
156
|
for (const queueMeta of Object.values(state.queueWorkers.meta)) {
|
|
162
|
-
expandAndAddGroupServices(queueMeta.middleware, state, addServices
|
|
163
|
-
// expandAndAddGroupServices(queueMeta.permissions, state, addServices, false)
|
|
157
|
+
expandAndAddGroupServices(queueMeta.middleware, state, addServices);
|
|
164
158
|
}
|
|
165
159
|
for (const scheduleMeta of Object.values(state.scheduledTasks.meta)) {
|
|
166
|
-
expandAndAddGroupServices(scheduleMeta.middleware, state, addServices
|
|
167
|
-
// expandAndAddGroupServices(scheduleMeta.permissions, state, addServices, false)
|
|
160
|
+
expandAndAddGroupServices(scheduleMeta.middleware, state, addServices);
|
|
168
161
|
}
|
|
169
162
|
for (const toolMeta of Object.values(state.mcpEndpoints.toolsMeta)) {
|
|
170
|
-
expandAndAddGroupServices(toolMeta.middleware, state, addServices
|
|
171
|
-
expandAndAddGroupServices(toolMeta.permissions, state, addServices, false);
|
|
163
|
+
expandAndAddGroupServices(toolMeta.middleware, state, addServices);
|
|
172
164
|
}
|
|
173
165
|
for (const promptMeta of Object.values(state.mcpEndpoints.promptsMeta)) {
|
|
174
|
-
expandAndAddGroupServices(promptMeta.middleware, state, addServices
|
|
175
|
-
expandAndAddGroupServices(promptMeta.permissions, state, addServices, false);
|
|
166
|
+
expandAndAddGroupServices(promptMeta.middleware, state, addServices);
|
|
176
167
|
}
|
|
177
168
|
for (const resourceMeta of Object.values(state.mcpEndpoints.resourcesMeta)) {
|
|
178
|
-
expandAndAddGroupServices(resourceMeta.middleware, state, addServices
|
|
179
|
-
expandAndAddGroupServices(resourceMeta.permissions, state, addServices, false);
|
|
169
|
+
expandAndAddGroupServices(resourceMeta.middleware, state, addServices);
|
|
180
170
|
}
|
|
181
171
|
// 5. Services from session service factories
|
|
182
172
|
for (const singletonServices of state.wireServicesMeta.values()) {
|
|
@@ -299,14 +289,17 @@ export function validateSecretOverrides(logger, state) {
|
|
|
299
289
|
const { wireAddonDeclarations } = state.rpc;
|
|
300
290
|
if (!wireAddonDeclarations || wireAddonDeclarations.size === 0)
|
|
301
291
|
return;
|
|
302
|
-
|
|
292
|
+
// secretOverrides key on (and resolve to) SECRET IDs — the string the addon
|
|
293
|
+
// passes to getSecret — so validate against secretId, falling back to name for
|
|
294
|
+
// older meta without a secretId field.
|
|
295
|
+
const secretIds = new Set(state.secrets.definitions.map((d) => d.secretId ?? d.name));
|
|
303
296
|
for (const [namespace, addonDecl] of wireAddonDeclarations.entries()) {
|
|
304
297
|
if (!addonDecl.secretOverrides)
|
|
305
298
|
continue;
|
|
306
|
-
for (const
|
|
307
|
-
if (!
|
|
308
|
-
const availableSecrets = Array.from(
|
|
309
|
-
logger.critical(ErrorCode.INVALID_VALUE, `Secret override '${
|
|
299
|
+
for (const [logicalName, resolvedName] of Object.entries(addonDecl.secretOverrides)) {
|
|
300
|
+
if (!secretIds.has(resolvedName)) {
|
|
301
|
+
const availableSecrets = Array.from(secretIds);
|
|
302
|
+
logger.critical(ErrorCode.INVALID_VALUE, `Secret override '${logicalName}' -> '${resolvedName}' in addon '${namespace}' (${addonDecl.package}) targets a secret that does not exist. Available secrets: ${availableSecrets.join(', ') || 'none'}`);
|
|
310
303
|
}
|
|
311
304
|
}
|
|
312
305
|
}
|
|
@@ -319,10 +312,10 @@ export function validateCredentialOverrides(logger, state) {
|
|
|
319
312
|
for (const [namespace, addonDecl] of wireAddonDeclarations.entries()) {
|
|
320
313
|
if (!addonDecl.credentialOverrides)
|
|
321
314
|
continue;
|
|
322
|
-
for (const
|
|
323
|
-
if (!credentialNames.has(
|
|
315
|
+
for (const [logicalName, resolvedName] of Object.entries(addonDecl.credentialOverrides)) {
|
|
316
|
+
if (!credentialNames.has(resolvedName)) {
|
|
324
317
|
const availableCredentials = Array.from(credentialNames);
|
|
325
|
-
logger.critical(ErrorCode.INVALID_VALUE, `Credential override '${
|
|
318
|
+
logger.critical(ErrorCode.INVALID_VALUE, `Credential override '${logicalName}' -> '${resolvedName}' in addon '${namespace}' (${addonDecl.package}) targets a credential that does not exist. Available credentials: ${availableCredentials.join(', ') || 'none'}`);
|
|
326
319
|
}
|
|
327
320
|
}
|
|
328
321
|
}
|
|
@@ -331,18 +324,82 @@ export function validateVariableOverrides(logger, state) {
|
|
|
331
324
|
const { wireAddonDeclarations } = state.rpc;
|
|
332
325
|
if (!wireAddonDeclarations || wireAddonDeclarations.size === 0)
|
|
333
326
|
return;
|
|
334
|
-
|
|
327
|
+
// variableOverrides key on (and resolve to) VARIABLE IDs, so validate against
|
|
328
|
+
// variableId, falling back to name for older meta without a variableId field.
|
|
329
|
+
const variableIds = new Set(state.variables.definitions.map((d) => d.variableId ?? d.name));
|
|
335
330
|
for (const [namespace, addonDecl] of wireAddonDeclarations.entries()) {
|
|
336
331
|
if (!addonDecl.variableOverrides)
|
|
337
332
|
continue;
|
|
338
|
-
for (const
|
|
339
|
-
if (!
|
|
340
|
-
const availableVariables = Array.from(
|
|
341
|
-
logger.critical(ErrorCode.INVALID_VALUE, `Variable override '${
|
|
333
|
+
for (const [logicalName, resolvedName] of Object.entries(addonDecl.variableOverrides)) {
|
|
334
|
+
if (!variableIds.has(resolvedName)) {
|
|
335
|
+
const availableVariables = Array.from(variableIds);
|
|
336
|
+
logger.critical(ErrorCode.INVALID_VALUE, `Variable override '${logicalName}' -> '${resolvedName}' in addon '${namespace}' (${addonDecl.package}) targets a variable that does not exist. Available variables: ${availableVariables.join(', ') || 'none'}`);
|
|
342
337
|
}
|
|
343
338
|
}
|
|
344
339
|
}
|
|
345
340
|
}
|
|
341
|
+
/**
|
|
342
|
+
* A `wireRemoteAddon` package ships types only — its handlers run on the host —
|
|
343
|
+
* so it MUST be a devDependency, not a production dependency (a prod dep would
|
|
344
|
+
* drag in the runtime deps remote consumption exists to avoid). This is the
|
|
345
|
+
* mirror image of `wireAddon`, which requires a production dependency.
|
|
346
|
+
*/
|
|
347
|
+
export function validateRemoteAddonDependencies(logger, state) {
|
|
348
|
+
const { wireAddonDeclarations } = state.rpc;
|
|
349
|
+
if (!wireAddonDeclarations || wireAddonDeclarations.size === 0)
|
|
350
|
+
return;
|
|
351
|
+
const hasRemote = Array.from(wireAddonDeclarations.values()).some((d) => d.remote);
|
|
352
|
+
if (!hasRemote)
|
|
353
|
+
return;
|
|
354
|
+
const pkgJsonPath = join(state.rootDir, 'package.json');
|
|
355
|
+
if (!existsSync(pkgJsonPath))
|
|
356
|
+
return; // no manifest to check (e.g. some tests)
|
|
357
|
+
let pkgJson;
|
|
358
|
+
try {
|
|
359
|
+
pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
|
|
360
|
+
}
|
|
361
|
+
catch (e) {
|
|
362
|
+
logger.warn(`Could not read ${pkgJsonPath} to verify remote addon dependencies: ${e?.message ?? e}`);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const prodDeps = pkgJson.dependencies ?? {};
|
|
366
|
+
const devDeps = pkgJson.devDependencies ?? {};
|
|
367
|
+
for (const [namespace, decl] of wireAddonDeclarations.entries()) {
|
|
368
|
+
if (!decl.remote)
|
|
369
|
+
continue;
|
|
370
|
+
if (decl.package in devDeps)
|
|
371
|
+
continue; // correct
|
|
372
|
+
if (decl.package in prodDeps) {
|
|
373
|
+
logger.critical(ErrorCode.REMOTE_ADDON_NOT_DEV_DEPENDENCY, `Remote addon '${namespace}' ('${decl.package}') is a production dependency, but wireRemoteAddon consumes it for types only — its handlers run on the host. Move '${decl.package}' from "dependencies" to "devDependencies".`);
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
logger.critical(ErrorCode.REMOTE_ADDON_NOT_DEV_DEPENDENCY, `Remote addon '${namespace}' ('${decl.package}') is wired with wireRemoteAddon but is not in "devDependencies". Add '${decl.package}' to "devDependencies" (types only).`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* The auth a `wireRemoteAddon` consumer binds must reference a slot the consumer
|
|
382
|
+
* actually declares: a `credentialId` must be a wired credential, a `secretId`
|
|
383
|
+
* a wired secret. A custom `resolve()` and a public (omitted) surface are not
|
|
384
|
+
* statically checkable and are left to runtime.
|
|
385
|
+
*/
|
|
386
|
+
export function validateRemoteAddonAuth(logger, state) {
|
|
387
|
+
const { wireAddonDeclarations } = state.rpc;
|
|
388
|
+
if (!wireAddonDeclarations || wireAddonDeclarations.size === 0)
|
|
389
|
+
return;
|
|
390
|
+
const credentialNames = new Set(state.credentials?.definitions.map((d) => d.name) ?? []);
|
|
391
|
+
const secretNames = new Set(state.secrets.definitions.map((d) => d.name));
|
|
392
|
+
for (const [namespace, decl] of wireAddonDeclarations.entries()) {
|
|
393
|
+
if (!decl.remote)
|
|
394
|
+
continue;
|
|
395
|
+
if (decl.authCredentialId && !credentialNames.has(decl.authCredentialId)) {
|
|
396
|
+
logger.critical(ErrorCode.REMOTE_ADDON_AUTH_UNRESOLVED, `Remote addon '${namespace}' binds auth.credentialId '${decl.authCredentialId}', but no such credential is wired. Available credentials: ${Array.from(credentialNames).join(', ') || 'none'}`);
|
|
397
|
+
}
|
|
398
|
+
if (decl.authSecretId && !secretNames.has(decl.authSecretId)) {
|
|
399
|
+
logger.critical(ErrorCode.REMOTE_ADDON_AUTH_UNRESOLVED, `Remote addon '${namespace}' binds auth.secretId '${decl.authSecretId}', but no such secret is wired. Available secrets: ${Array.from(secretNames).join(', ') || 'none'}`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
346
403
|
export function computeResolvedIOTypes(state) {
|
|
347
404
|
const { functions } = state;
|
|
348
405
|
for (const [pikkuFuncId, meta] of Object.entries(functions.meta)) {
|
|
@@ -424,34 +481,8 @@ export function computeMiddlewareGroupsMeta(state) {
|
|
|
424
481
|
};
|
|
425
482
|
}
|
|
426
483
|
export function computePermissionsGroupsMeta(state) {
|
|
427
|
-
const httpGroups = {};
|
|
428
|
-
for (const [pattern, meta] of state.http.routePermissions.entries()) {
|
|
429
|
-
httpGroups[pattern] = {
|
|
430
|
-
exportName: meta.exportName,
|
|
431
|
-
sourceFile: meta.sourceFile,
|
|
432
|
-
position: meta.position,
|
|
433
|
-
services: meta.services,
|
|
434
|
-
count: meta.count,
|
|
435
|
-
instanceIds: meta.instanceIds,
|
|
436
|
-
isFactory: meta.isFactory,
|
|
437
|
-
};
|
|
438
|
-
}
|
|
439
|
-
const tagGroups = {};
|
|
440
|
-
for (const [tag, meta] of state.permissions.tagPermissions.entries()) {
|
|
441
|
-
tagGroups[tag] = {
|
|
442
|
-
exportName: meta.exportName,
|
|
443
|
-
sourceFile: meta.sourceFile,
|
|
444
|
-
position: meta.position,
|
|
445
|
-
services: meta.services,
|
|
446
|
-
count: meta.count,
|
|
447
|
-
instanceIds: meta.instanceIds,
|
|
448
|
-
isFactory: meta.isFactory,
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
484
|
state.permissionsGroupsMeta = {
|
|
452
485
|
definitions: state.permissions.definitions,
|
|
453
|
-
httpGroups,
|
|
454
|
-
tagGroups,
|
|
455
486
|
};
|
|
456
487
|
}
|
|
457
488
|
const PRIMITIVE_TYPES = new Set([
|
|
@@ -508,6 +539,34 @@ export function validateAgentModels(logger, state) {
|
|
|
508
539
|
}
|
|
509
540
|
}
|
|
510
541
|
}
|
|
542
|
+
/**
|
|
543
|
+
* A `pikkuWorkflowGraph` node that references the `graph:` namespace (e.g.
|
|
544
|
+
* `graph:editFields`) needs @pikku/addon-graph wired — otherwise the RPC never
|
|
545
|
+
* registers and codegen fails deep in type-checking with an opaque error. Fail
|
|
546
|
+
* early with an actionable message pointing at `scaffold: { graph: true }`.
|
|
547
|
+
*/
|
|
548
|
+
export function validateWorkflowGraphAddons(logger, state) {
|
|
549
|
+
const addonGraphWired = Array.from(state.rpc.wireAddonDeclarations.values()).some((decl) => decl.package === '@pikku/addon-graph');
|
|
550
|
+
if (addonGraphWired) {
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
for (const [name, graph] of Object.entries(state.workflows.graphMeta)) {
|
|
554
|
+
for (const node of Object.values(graph.nodes)) {
|
|
555
|
+
if (!('rpcName' in node) || typeof node.rpcName !== 'string') {
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
if (!node.rpcName.startsWith('graph:')) {
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
if (state.functions.meta[node.rpcName]) {
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
logger.critical(ErrorCode.WORKFLOW_GRAPH_ADDON_NOT_WIRED, `Workflow graph '${name}' references '${node.rpcName}' but @pikku/addon-graph is not wired. ` +
|
|
565
|
+
`Enable "scaffold": { "graph": true } in pikku.config.json (and install @pikku/addon-graph), ` +
|
|
566
|
+
`or wire it manually with wireAddon({ name: 'graph', package: '@pikku/addon-graph' }).`);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
511
570
|
/**
|
|
512
571
|
* Validates that Zod schemas and wiring side-effects (wireHTTPRoutes,
|
|
513
572
|
* addPermission, addHTTPMiddleware, etc.) do not coexist in the same file.
|
|
@@ -528,13 +587,6 @@ export function validateSchemaWiringSeparation(logger, state) {
|
|
|
528
587
|
for (const file of state.http.files) {
|
|
529
588
|
wiringFiles.add(file);
|
|
530
589
|
}
|
|
531
|
-
// Permission wirings (addPermission calls)
|
|
532
|
-
for (const meta of state.permissions.tagPermissions.values()) {
|
|
533
|
-
wiringFiles.add(meta.sourceFile);
|
|
534
|
-
}
|
|
535
|
-
for (const meta of state.http.routePermissions.values()) {
|
|
536
|
-
wiringFiles.add(meta.sourceFile);
|
|
537
|
-
}
|
|
538
590
|
// Middleware wirings (addHTTPMiddleware calls)
|
|
539
591
|
for (const meta of state.http.routeMiddleware.values()) {
|
|
540
592
|
wiringFiles.add(meta.sourceFile);
|
|
@@ -628,3 +680,29 @@ export function computeDiagnostics(state) {
|
|
|
628
680
|
}
|
|
629
681
|
state.diagnostics = diagnostics;
|
|
630
682
|
}
|
|
683
|
+
/**
|
|
684
|
+
* Validates that every scope referenced by a function is declared via
|
|
685
|
+
* `wireScope`. Runs after all visitors, so declaration order does not matter.
|
|
686
|
+
*
|
|
687
|
+
* A `*` suffix is a wildcard requirement: `admin:*` requires the `admin` scope
|
|
688
|
+
* to be declared, and grants its whole subtree.
|
|
689
|
+
*/
|
|
690
|
+
export function validateScopeReferences(logger, state) {
|
|
691
|
+
const declared = new Set(flattenScopeDefinitions(state.scopes.definitions).map((s) => s.id));
|
|
692
|
+
for (const [funcName, meta] of Object.entries(state.functions.meta)) {
|
|
693
|
+
if (!meta.scopes?.length)
|
|
694
|
+
continue;
|
|
695
|
+
for (const scope of meta.scopes) {
|
|
696
|
+
// A trailing wildcard grants a subtree; the node it hangs off must exist.
|
|
697
|
+
const declaredForm = scope.endsWith(':*') ? scope.slice(0, -2) : scope;
|
|
698
|
+
if (scope === '*') {
|
|
699
|
+
logger.critical(ErrorCode.INVALID_VALUE, `Function '${funcName}' requires the bare wildcard scope '*'. A function must require a specific scope — '*' is only meaningful as a grant.`);
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
if (!declared.has(declaredForm)) {
|
|
703
|
+
const available = Array.from(declared);
|
|
704
|
+
logger.critical(ErrorCode.INVALID_VALUE, `Function '${funcName}' requires scope '${scope}' which is not declared. Declare it with wireScope. Available scopes: ${available.join(', ') || 'none'}`);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
@@ -67,7 +67,9 @@ const inspectorVersion = (() => {
|
|
|
67
67
|
try {
|
|
68
68
|
const pkgUrl = new URL('../../package.json', import.meta.url);
|
|
69
69
|
const pkg = JSON.parse(readFileSync(pkgUrl, 'utf-8'));
|
|
70
|
-
return typeof pkg.version === 'string'
|
|
70
|
+
return typeof pkg.version === 'string'
|
|
71
|
+
? pkg.version
|
|
72
|
+
: `v${SCHEMA_CACHE_VERSION}`;
|
|
71
73
|
}
|
|
72
74
|
catch {
|
|
73
75
|
return `v${SCHEMA_CACHE_VERSION}`;
|
|
@@ -112,7 +114,7 @@ function writeDiskTSSchemas(logger, cacheDir, key, schemas) {
|
|
|
112
114
|
logger.debug(`Failed to persist TS-schema cache: ${e.message}`);
|
|
113
115
|
}
|
|
114
116
|
}
|
|
115
|
-
function createProgramWithVirtualFile(tsconfig, virtualFilePath, virtualFileContent) {
|
|
117
|
+
function createProgramWithVirtualFile(logger, tsconfig, virtualFilePath, virtualFileContent) {
|
|
116
118
|
const configPath = resolve(tsconfig);
|
|
117
119
|
// Cache the parsed tsconfig — it doesn't change between runs
|
|
118
120
|
if (!cachedParsedConfig || cachedTsconfigPath !== configPath) {
|
|
@@ -123,7 +125,11 @@ function createProgramWithVirtualFile(tsconfig, virtualFilePath, virtualFileCont
|
|
|
123
125
|
cachedSchemaProgram = undefined;
|
|
124
126
|
}
|
|
125
127
|
const resolvedVirtualPath = resolve(virtualFilePath);
|
|
126
|
-
|
|
128
|
+
// The virtual file imports every type it references, so it is a complete root
|
|
129
|
+
// on its own and TypeScript pulls in exactly the transitive closure it needs.
|
|
130
|
+
// Rooting at cachedParsedConfig.fileNames instead loaded the whole project
|
|
131
|
+
// (2572 files vs 870 on a real tree) without widening what could be resolved.
|
|
132
|
+
const fileNames = [resolvedVirtualPath];
|
|
127
133
|
const defaultHost = ts.createCompilerHost(cachedParsedConfig.options);
|
|
128
134
|
const customHost = {
|
|
129
135
|
...defaultHost,
|
|
@@ -144,9 +150,11 @@ function createProgramWithVirtualFile(tsconfig, virtualFilePath, virtualFileCont
|
|
|
144
150
|
return defaultHost.readFile(fileName);
|
|
145
151
|
},
|
|
146
152
|
};
|
|
153
|
+
const startProgram = performance.now();
|
|
147
154
|
const program = ts.createProgram(fileNames, cachedParsedConfig.options, customHost, cachedSchemaProgram // reuse previous program for incremental compilation
|
|
148
155
|
);
|
|
149
156
|
cachedSchemaProgram = program;
|
|
157
|
+
logger.debug(`Created schema program in ${(performance.now() - startProgram).toFixed(0)}ms (${program.getSourceFiles().length} files)`);
|
|
150
158
|
return program;
|
|
151
159
|
}
|
|
152
160
|
function generateTSSchemas(logger, tsconfig, customTypesContent, typesMap, functionMeta, httpWiringsMeta, additionalTypes, additionalProperties = false, generatedZodSchemas) {
|
|
@@ -192,7 +200,7 @@ function generateTSSchemas(logger, tsconfig, customTypesContent, typesMap, funct
|
|
|
192
200
|
}
|
|
193
201
|
logger.debug(`generateTSSchemas needed for ${uncoveredSchemas.length} types: ${uncoveredSchemas.slice(0, 3).join(', ')}${uncoveredSchemas.length > 3 ? '...' : ''}`);
|
|
194
202
|
const virtualFilePath = join(dirname(resolve(tsconfig)), '__pikku_virtual_types__.ts');
|
|
195
|
-
const program = createProgramWithVirtualFile(tsconfig, virtualFilePath, customTypesContent);
|
|
203
|
+
const program = createProgramWithVirtualFile(logger, tsconfig, virtualFilePath, customTypesContent);
|
|
196
204
|
const generator = createGenerator({
|
|
197
205
|
tsProgram: program,
|
|
198
206
|
skipTypeCheck: true,
|
|
@@ -408,6 +416,13 @@ export async function generateAllSchemas(logger, config, state) {
|
|
|
408
416
|
}
|
|
409
417
|
}
|
|
410
418
|
const tsSchemas = generateTSSchemas(logger, config.tsconfig, customTypesContent, state.functions.typesMap, state.functions.meta, state.http.meta, config.schemasFromTypes, config.schema?.additionalProperties, zodSchemas);
|
|
419
|
+
// Release the program once the schemas are out of it. It spans the whole
|
|
420
|
+
// tsconfig (~2.5k files, ~600MB on a large tree) and was previously pinned at
|
|
421
|
+
// module scope for the life of the process, so it stacked on top of the main
|
|
422
|
+
// inspector program and pushed `pikku all` past a 2GB heap (#982). The result
|
|
423
|
+
// cache below is what the same-process fast path actually needs; the program
|
|
424
|
+
// only bought incremental reuse on a rebuild, at a cost that dwarfs it.
|
|
425
|
+
cachedSchemaProgram = undefined;
|
|
411
426
|
cachedCustomTypesContent = customTypesContent;
|
|
412
427
|
cachedTSSchemas = tsSchemas;
|
|
413
428
|
if (config.cacheDir && cacheKey) {
|
|
@@ -127,10 +127,6 @@ export interface SerializableInspectorState {
|
|
|
127
127
|
string,
|
|
128
128
|
InspectorState['http']['routeMiddleware'] extends Map<string, infer V> ? V : never
|
|
129
129
|
]>;
|
|
130
|
-
routePermissions: Array<[
|
|
131
|
-
string,
|
|
132
|
-
InspectorState['http']['routePermissions'] extends Map<string, infer V> ? V : never
|
|
133
|
-
]>;
|
|
134
130
|
};
|
|
135
131
|
channels: {
|
|
136
132
|
files: string[];
|
|
@@ -177,6 +173,11 @@ export interface SerializableInspectorState {
|
|
|
177
173
|
path: string;
|
|
178
174
|
exportedName: string;
|
|
179
175
|
}]>;
|
|
176
|
+
remoteMeta: InspectorState['rpc']['remoteMeta'];
|
|
177
|
+
remoteFiles: Array<[string, {
|
|
178
|
+
path: string;
|
|
179
|
+
exportedName: string;
|
|
180
|
+
}]>;
|
|
180
181
|
invokedFunctions: string[];
|
|
181
182
|
invokedFunctionsByFile?: Array<[string, string[]]>;
|
|
182
183
|
usedAddons: string[];
|
|
@@ -185,6 +186,10 @@ export interface SerializableInspectorState {
|
|
|
185
186
|
{
|
|
186
187
|
package: string;
|
|
187
188
|
rpcEndpoint?: string;
|
|
189
|
+
remote?: boolean;
|
|
190
|
+
hasAuth?: boolean;
|
|
191
|
+
authCredentialId?: string;
|
|
192
|
+
authSecretId?: string;
|
|
188
193
|
secretOverrides?: Record<string, string>;
|
|
189
194
|
variableOverrides?: Record<string, string>;
|
|
190
195
|
credentialOverrides?: Record<string, string>;
|
|
@@ -227,6 +232,10 @@ export interface SerializableInspectorState {
|
|
|
227
232
|
definitions: InspectorState['credentials']['definitions'];
|
|
228
233
|
files: string[];
|
|
229
234
|
};
|
|
235
|
+
scopes: {
|
|
236
|
+
definitions: InspectorState['scopes']['definitions'];
|
|
237
|
+
files: string[];
|
|
238
|
+
};
|
|
230
239
|
variables: {
|
|
231
240
|
definitions: InspectorState['variables']['definitions'];
|
|
232
241
|
files: string[];
|
|
@@ -254,10 +263,6 @@ export interface SerializableInspectorState {
|
|
|
254
263
|
permissions: {
|
|
255
264
|
definitions: InspectorState['permissions']['definitions'];
|
|
256
265
|
instances: InspectorState['permissions']['instances'];
|
|
257
|
-
tagPermissions: Array<[
|
|
258
|
-
string,
|
|
259
|
-
InspectorState['permissions']['tagPermissions'] extends Map<string, infer V> ? V : never
|
|
260
|
-
]>;
|
|
261
266
|
};
|
|
262
267
|
serviceAggregation: {
|
|
263
268
|
requiredServices: string[];
|
|
@@ -42,7 +42,6 @@ export function serializeInspectorState(state) {
|
|
|
42
42
|
meta: state.http.meta,
|
|
43
43
|
files: Array.from(state.http.files),
|
|
44
44
|
routeMiddleware: Array.from(state.http.routeMiddleware.entries()),
|
|
45
|
-
routePermissions: Array.from(state.http.routePermissions.entries()),
|
|
46
45
|
},
|
|
47
46
|
channels: {
|
|
48
47
|
files: Array.from(state.channels.files),
|
|
@@ -77,6 +76,8 @@ export function serializeInspectorState(state) {
|
|
|
77
76
|
internalFiles: Array.from(state.rpc.internalFiles.entries()),
|
|
78
77
|
exposedMeta: state.rpc.exposedMeta,
|
|
79
78
|
exposedFiles: Array.from(state.rpc.exposedFiles.entries()),
|
|
79
|
+
remoteMeta: state.rpc.remoteMeta,
|
|
80
|
+
remoteFiles: Array.from(state.rpc.remoteFiles.entries()),
|
|
80
81
|
invokedFunctions: Array.from(state.rpc.invokedFunctions),
|
|
81
82
|
invokedFunctionsByFile: Array.from((state.rpc.invokedFunctionsByFile ?? new Map()).entries()).map(([file, fns]) => [file, Array.from(fns)]),
|
|
82
83
|
usedAddons: Array.from(state.rpc.usedAddons),
|
|
@@ -115,6 +116,10 @@ export function serializeInspectorState(state) {
|
|
|
115
116
|
definitions: state.credentials.definitions,
|
|
116
117
|
files: Array.from(state.credentials.files),
|
|
117
118
|
},
|
|
119
|
+
scopes: {
|
|
120
|
+
definitions: state.scopes.definitions,
|
|
121
|
+
files: Array.from(state.scopes.files),
|
|
122
|
+
},
|
|
118
123
|
variables: {
|
|
119
124
|
definitions: state.variables.definitions,
|
|
120
125
|
files: Array.from(state.variables.files),
|
|
@@ -136,7 +141,6 @@ export function serializeInspectorState(state) {
|
|
|
136
141
|
permissions: {
|
|
137
142
|
definitions: state.permissions.definitions,
|
|
138
143
|
instances: state.permissions.instances,
|
|
139
|
-
tagPermissions: Array.from(state.permissions.tagPermissions.entries()),
|
|
140
144
|
},
|
|
141
145
|
serviceAggregation: {
|
|
142
146
|
requiredServices: Array.from(state.serviceAggregation.requiredServices),
|
|
@@ -201,7 +205,6 @@ export function deserializeInspectorState(data) {
|
|
|
201
205
|
meta: data.http.meta,
|
|
202
206
|
files: new Set(data.http.files),
|
|
203
207
|
routeMiddleware: new Map(data.http.routeMiddleware),
|
|
204
|
-
routePermissions: new Map(data.http.routePermissions),
|
|
205
208
|
},
|
|
206
209
|
channels: {
|
|
207
210
|
files: new Set(data.channels.files),
|
|
@@ -236,6 +239,8 @@ export function deserializeInspectorState(data) {
|
|
|
236
239
|
internalFiles: new Map(data.rpc.internalFiles),
|
|
237
240
|
exposedMeta: data.rpc.exposedMeta,
|
|
238
241
|
exposedFiles: new Map(data.rpc.exposedFiles),
|
|
242
|
+
remoteMeta: data.rpc.remoteMeta ?? {},
|
|
243
|
+
remoteFiles: new Map(data.rpc.remoteFiles ?? []),
|
|
239
244
|
invokedFunctions: new Set(data.rpc.invokedFunctions),
|
|
240
245
|
invokedFunctionsByFile: new Map((data.rpc.invokedFunctionsByFile || []).map(([file, fns]) => [file, new Set(fns)])),
|
|
241
246
|
usedAddons: new Set(data.rpc.usedAddons || []),
|
|
@@ -274,6 +279,10 @@ export function deserializeInspectorState(data) {
|
|
|
274
279
|
definitions: data.credentials?.definitions || [],
|
|
275
280
|
files: new Set(data.credentials?.files || []),
|
|
276
281
|
},
|
|
282
|
+
scopes: {
|
|
283
|
+
definitions: data.scopes?.definitions || [],
|
|
284
|
+
files: new Set(data.scopes?.files || []),
|
|
285
|
+
},
|
|
277
286
|
variables: {
|
|
278
287
|
definitions: data.variables?.definitions || [],
|
|
279
288
|
files: new Set(data.variables?.files || []),
|
|
@@ -295,7 +304,6 @@ export function deserializeInspectorState(data) {
|
|
|
295
304
|
permissions: {
|
|
296
305
|
definitions: data.permissions.definitions,
|
|
297
306
|
instances: data.permissions.instances || {},
|
|
298
|
-
tagPermissions: new Map(data.permissions.tagPermissions),
|
|
299
307
|
},
|
|
300
308
|
serviceAggregation: {
|
|
301
309
|
requiredServices: new Set(data.serviceAggregation.requiredServices),
|
|
@@ -315,8 +323,6 @@ export function deserializeInspectorState(data) {
|
|
|
315
323
|
},
|
|
316
324
|
permissionsGroupsMeta: data.permissionsGroupsMeta || {
|
|
317
325
|
definitions: {},
|
|
318
|
-
httpGroups: {},
|
|
319
|
-
tagGroups: {},
|
|
320
326
|
},
|
|
321
327
|
requiredSchemas: new Set(data.requiredSchemas || []),
|
|
322
328
|
openAPISpec: data.openAPISpec || null,
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import type { InspectorState } from '../types.js';
|
|
2
2
|
export declare const serializePermissionsGroupsMeta: (state: InspectorState) => {
|
|
3
3
|
definitions: Record<string, import("../types.js").InspectorPermissionDefinition>;
|
|
4
|
-
httpGroups: Record<string, any>;
|
|
5
|
-
tagGroups: Record<string, any>;
|
|
6
4
|
};
|
|
@@ -1,31 +1,5 @@
|
|
|
1
1
|
export const serializePermissionsGroupsMeta = (state) => {
|
|
2
|
-
const httpGroups = {};
|
|
3
|
-
for (const [pattern, meta] of state.http.routePermissions.entries()) {
|
|
4
|
-
httpGroups[pattern] = {
|
|
5
|
-
exportName: meta.exportName,
|
|
6
|
-
sourceFile: meta.sourceFile,
|
|
7
|
-
position: meta.position,
|
|
8
|
-
services: meta.services,
|
|
9
|
-
count: meta.count,
|
|
10
|
-
instanceIds: meta.instanceIds,
|
|
11
|
-
isFactory: meta.isFactory,
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
const tagGroups = {};
|
|
15
|
-
for (const [tag, meta] of state.permissions.tagPermissions.entries()) {
|
|
16
|
-
tagGroups[tag] = {
|
|
17
|
-
exportName: meta.exportName,
|
|
18
|
-
sourceFile: meta.sourceFile,
|
|
19
|
-
position: meta.position,
|
|
20
|
-
services: meta.services,
|
|
21
|
-
count: meta.count,
|
|
22
|
-
instanceIds: meta.instanceIds,
|
|
23
|
-
isFactory: meta.isFactory,
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
2
|
return {
|
|
27
3
|
definitions: state.permissions.definitions,
|
|
28
|
-
httpGroups,
|
|
29
|
-
tagGroups,
|
|
30
4
|
};
|
|
31
5
|
};
|
|
@@ -48,9 +48,7 @@ function containsConditional(steps) {
|
|
|
48
48
|
* e.g. "awaiting_approval" → "Awaiting approval", "building-image" → "Building image"
|
|
49
49
|
*/
|
|
50
50
|
function reasonToLabel(reason) {
|
|
51
|
-
return reason
|
|
52
|
-
.replace(/[-_]/g, ' ')
|
|
53
|
-
.replace(/^(.)/, (c) => c.toUpperCase());
|
|
51
|
+
return reason.replace(/[-_]/g, ' ').replace(/^(.)/, (c) => c.toUpperCase());
|
|
54
52
|
}
|
|
55
53
|
/** Flatten named steps (rpc/inline/sleep/parallel children) in source order. */
|
|
56
54
|
function collectNamedSteps(steps) {
|
|
@@ -72,6 +70,14 @@ function collectNamedSteps(steps) {
|
|
|
72
70
|
displayName: reasonToLabel(step.reason),
|
|
73
71
|
});
|
|
74
72
|
break;
|
|
73
|
+
case 'approval':
|
|
74
|
+
// Same as suspend, but namespaced `__workflow_approval:` to match the
|
|
75
|
+
// runtime's separate step key.
|
|
76
|
+
planned.push({
|
|
77
|
+
stepName: `__workflow_approval:${step.reason}`,
|
|
78
|
+
displayName: reasonToLabel(step.reason),
|
|
79
|
+
});
|
|
80
|
+
break;
|
|
75
81
|
case 'parallel':
|
|
76
82
|
for (const child of step.children) {
|
|
77
83
|
planned.push({ stepName: child.stepName });
|