@pikku/inspector 0.12.94 → 0.12.96

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 CHANGED
@@ -1,3 +1,32 @@
1
+ ## 0.12.96
2
+
3
+ ### Patch Changes
4
+
5
+ - dfcd351: The deploy planner now binds a unit to the units of the functions its functions call with `rpc.invoke('name')` / `rpc.remote('name')`. Before, a caller and callee split into different units (e.g. a no-service function in `svc-base` calling a DB function in `svc-kysely`) got no service binding and failed on the deployed stage with "No service binding for function". The inspector records literal RPC names per function as `invokes` (single, double or substitution-free template quotes; `rpc!`, `wire.rpc`) and warns on a computed name, which the planner cannot see (#1883).
6
+ - cf40182: Deployed units that call `rpc.startWorkflow('x')` now get x's meta, so the call no longer fails with `WorkflowNotFoundError`. The inspector records `startsWorkflows` for literal `rpc.startWorkflow(...)` calls. It warns when a handler computes the workflow name or passes `rpc` to a helper, because the planner cannot see those calls.
7
+
8
+ With workflow queues, the deploy planner gives a starter unit the workflow meta and orchestrator queue meta only (new `--workflowMeta` filter), plus `workflow-state` and `queue` services. Without queues the start runs inline, so the whole workflow is bundled. Core's `startWorkflow` now requires the workflow registration only for inline runs; queued runs need only the meta.
9
+
10
+ - f817f1c: Schema generation no longer re-walks every source file for each type. ts-json-schema-generator looks each root type up by name by scanning the whole program, so a project with 677 types scanned it 677 times. The inspector now builds that name index once per program. On the e2e project, TS schema generation drops from 8.7s to 0.1s and a cold `pikku all` from 38s to 8s, with identical output.
11
+ - 5bce779: Webhook trigger sources are off until someone turns them on, and addons declare their own.
12
+
13
+ An addon calls `wireTriggerWebhookSource` in its own package, and an app that wires the addon gets the source (its route included) without declaring it: named and routed after the addon's namespace, so two instances get one each. A source the app declares under the same name wins.
14
+
15
+ Every source now has an `enabled` switch in the `triggerSourceStore`, off by default. `reconcileTriggerSources` registers only enabled sources and records each one's `baseUrl` and `labelPrefix`; a disabled source's route answers 404 without running `receive`. New in core: `enableTriggerSource` registers a source with its provider and `disableTriggerSource` stops it receiving, then tears it down, both at the recorded address unless one is given. The admin addon exposes them as `triggerSourceEnable` and `triggerSourceDisable`. The `pikkuTriggerSource` table gains `enabled`, `baseUrl` and `labelPrefix`: run `pikku db generate` for the migration.
16
+
17
+ - Updated dependencies [dfcd351]
18
+ - Updated dependencies [cf40182]
19
+ - Updated dependencies [5bce779]
20
+ - Updated dependencies [5bce779]
21
+ - @pikku/core@0.12.130
22
+
23
+ ## 0.12.95
24
+
25
+ ### Patch Changes
26
+
27
+ - 6ecf80e: Stop the services-destructure lint (PKU410) firing on the stub a wiring registers for its function. Since inline functions carry a source file, a queue worker wired to a named function failed the build with a critical PKU410 even though that function destructures its services.
28
+ - 2612a67: The services-destructure lint also skips the `helper` wiring functions `ensureFunctionMetadata` synthesizes, alongside the `inline` ones it already skips, and a test pins that an inline wiring function that records a source file is not flagged.
29
+
1
30
  ## 0.12.94
2
31
 
3
32
  ### Patch Changes
@@ -3,6 +3,8 @@ import { detectSchemaVendorOrError } from '../utils/detect-schema-vendor.js';
3
3
  import { extractFunctionName, funcIdToTypeName, } from '../utils/extract-function-name.js';
4
4
  import { extractFunctionNode } from '../utils/extract-function-node.js';
5
5
  import { extractUsedWires } from '../utils/extract-services.js';
6
+ import { collectInvokedRpcNames } from './add-rpc-invocations.js';
7
+ import { collectStartedWorkflows } from './collect-started-workflows.js';
6
8
  import { formatVersionedId, parseVersionedId } from '@pikku/core/utils';
7
9
  /** Binding keys a `pikkuScenarioStep` may declare, in report order. */
8
10
  const SCENARIO_SURFACE_KEYS = [
@@ -720,6 +722,10 @@ export const addFunctions = (logger, node, checker, state, options) => {
720
722
  }
721
723
  }
722
724
  const wires = extractUsedWires(handler, 2);
725
+ const invoked = collectInvokedRpcNames(handler.body);
726
+ for (const call of invoked.dynamic) {
727
+ logger.warn(`• ${pikkuFuncId} calls ${call} with a computed name — the deploy planner cannot bind its unit to the callee's, so the call fails with "No service binding" if the two are deployed apart. Use a literal RPC name.`);
728
+ }
723
729
  // --- Generics → ts.Type[], unwrapped from Promise ---
724
730
  const genericTypes = (typeArguments ?? [])
725
731
  .map((tn) => checker.getTypeFromTypeNode(tn))
@@ -986,6 +992,17 @@ export const addFunctions = (logger, node, checker, state, options) => {
986
992
  bodyStart: lineOf(handlerBody.getStart(handlerSourceFile)),
987
993
  bodyEnd: lineOf(handlerBody.getEnd()),
988
994
  };
995
+ const started = collectStartedWorkflows(handler.body);
996
+ // Generated scaffolds (the workflow start/run routes) take the name from the
997
+ // request by design, and are served by units that have every workflow.
998
+ const handlerFile = handler.getSourceFile().fileName;
999
+ const warnOnStarts = !/\.gen\.[cm]?[jt]s$/.test(handlerFile);
1000
+ for (const call of warnOnStarts ? started.dynamic : []) {
1001
+ logger.warn(`• ${pikkuFuncId} calls ${call} with a computed name — the deploy planner cannot bundle that workflow's meta into ${pikkuFuncId}'s unit, so the deployed call fails with "Workflow not found" unless the unit has it for another reason. Use a literal workflow name.`);
1002
+ }
1003
+ if (warnOnStarts && started.rpcHandoffs.length > 0) {
1004
+ logger.warn(`• ${pikkuFuncId} passes rpc to ${started.rpcHandoffs.join(', ')} — rpc.startWorkflow / rpc.invoke calls made there are invisible to the deploy planner, so their targets are not bundled into or bound to ${pikkuFuncId}'s unit. Make those calls in the handler itself.`);
1005
+ }
989
1006
  state.functions.meta[pikkuFuncId] = {
990
1007
  pikkuFuncId,
991
1008
  functionType: 'user',
@@ -994,6 +1011,7 @@ export const addFunctions = (logger, node, checker, state, options) => {
994
1011
  name,
995
1012
  services,
996
1013
  wires: wires.wires.length > 0 || !wires.optimized ? wires : undefined,
1014
+ invokes: invoked.names.length > 0 ? invoked.names : undefined,
997
1015
  inputSchemaName: inputNames[0] ?? null,
998
1016
  outputSchemaName: outputNames[0] ?? null,
999
1017
  inputs: inputNames.filter((n) => n !== 'void') ?? null,
@@ -1037,6 +1055,7 @@ export const addFunctions = (logger, node, checker, state, options) => {
1037
1055
  bodySourceFile,
1038
1056
  exportedName: exportedName || undefined,
1039
1057
  ...bodySpan,
1058
+ startsWorkflows: started.names.length > 0 ? started.names : undefined,
1040
1059
  };
1041
1060
  if (handlerHasDynamicImport(handler)) {
1042
1061
  state.functions.dynamicImportIds.add(pikkuFuncId);
@@ -1,5 +1,22 @@
1
1
  import * as ts from 'typescript';
2
2
  import type { InspectorState, InspectorLogger } from '../types.js';
3
+ /**
4
+ * The RPC names a function handler calls through `rpc.invoke(...)` /
5
+ * `rpc.remote(...)`, read from its own body.
6
+ *
7
+ * This is what the deploy planner uses to bind a unit to the units its
8
+ * functions call: without it, a caller and callee split into different units
9
+ * type-check, work in a single process, and fail on the deployed stage with
10
+ * "No service binding for function". `dynamic` holds the source text of calls
11
+ * whose name is not a literal — those cannot be planned for.
12
+ *
13
+ * Only the handler's own body is read. A call made from a helper defined
14
+ * elsewhere, with `rpc` passed in, is not seen.
15
+ */
16
+ export declare function collectInvokedRpcNames(handler: ts.Node): {
17
+ names: string[];
18
+ dynamic: string[];
19
+ };
3
20
  /**
4
21
  * Scan for rpc.invoke() calls to track which functions are actually being invoked
5
22
  * Also detects addon usage via:
@@ -32,6 +32,63 @@ function invocationReceiver(node) {
32
32
  }
33
33
  return inner;
34
34
  }
35
+ /** `rpc`, or anything ending in `.rpc` (`wire.rpc`), seen through `!` and parens. */
36
+ function isRpcReceiver(node) {
37
+ const receiver = invocationReceiver(node);
38
+ return ts.isIdentifier(receiver)
39
+ ? receiver.text === 'rpc'
40
+ : ts.isPropertyAccessExpression(receiver) && receiver.name.text === 'rpc';
41
+ }
42
+ /**
43
+ * Methods on `rpc` that call another function by name and, when that function
44
+ * is not bundled in the caller's deploy unit, cross to another unit through the
45
+ * `DeploymentService`.
46
+ */
47
+ const CROSS_UNIT_RPC_METHODS = new Set(['invoke', 'remote']);
48
+ /** The name argument when it is statically known: `'x'`, `"x"` or `` `x` ``. */
49
+ function literalRpcName(arg) {
50
+ if (ts.isStringLiteral(arg) || ts.isNoSubstitutionTemplateLiteral(arg)) {
51
+ return arg.text;
52
+ }
53
+ return undefined;
54
+ }
55
+ /**
56
+ * The RPC names a function handler calls through `rpc.invoke(...)` /
57
+ * `rpc.remote(...)`, read from its own body.
58
+ *
59
+ * This is what the deploy planner uses to bind a unit to the units its
60
+ * functions call: without it, a caller and callee split into different units
61
+ * type-check, work in a single process, and fail on the deployed stage with
62
+ * "No service binding for function". `dynamic` holds the source text of calls
63
+ * whose name is not a literal — those cannot be planned for.
64
+ *
65
+ * Only the handler's own body is read. A call made from a helper defined
66
+ * elsewhere, with `rpc` passed in, is not seen.
67
+ */
68
+ export function collectInvokedRpcNames(handler) {
69
+ const names = new Set();
70
+ const dynamic = [];
71
+ const visit = (node) => {
72
+ if (ts.isCallExpression(node) &&
73
+ ts.isPropertyAccessExpression(node.expression) &&
74
+ CROSS_UNIT_RPC_METHODS.has(node.expression.name.text) &&
75
+ isRpcReceiver(node.expression.expression)) {
76
+ const [firstArg] = node.arguments;
77
+ if (firstArg) {
78
+ const name = literalRpcName(firstArg);
79
+ if (name !== undefined) {
80
+ names.add(name);
81
+ }
82
+ else {
83
+ dynamic.push(node.getText());
84
+ }
85
+ }
86
+ }
87
+ ts.forEachChild(node, visit);
88
+ };
89
+ visit(handler);
90
+ return { names: [...names].sort(), dynamic };
91
+ }
35
92
  /**
36
93
  * Helper to extract namespace from a namespaced function reference like 'ext:hello'
37
94
  */
@@ -152,8 +209,9 @@ export function addRPCInvocations(node, state, logger) {
152
209
  }
153
210
  const [firstArg] = args;
154
211
  if (firstArg) {
155
- if (ts.isStringLiteral(firstArg)) {
156
- const functionRef = firstArg.text;
212
+ const literalName = literalRpcName(firstArg);
213
+ if (literalName !== undefined) {
214
+ const functionRef = literalName;
157
215
  logger.debug(`• Found RPC invocation: ${functionRef}`);
158
216
  state.rpc.invokedFunctions.add(functionRef);
159
217
  let byFile = state.rpc.invokedFunctionsByFile.get(sourceFileName);
@@ -170,10 +228,9 @@ export function addRPCInvocations(node, state, logger) {
170
228
  }
171
229
  }
172
230
  // Handle template literals like `function-${name}`
173
- else if (ts.isTemplateExpression(firstArg) ||
174
- ts.isNoSubstitutionTemplateLiteral(firstArg)) {
231
+ else if (ts.isTemplateExpression(firstArg)) {
175
232
  logger.warn(`• Found dynamic RPC invocation: ${firstArg.getText()}`);
176
- logger.warn(`\tYou can only use string literals for RPC function names, with ' or " and not \``);
233
+ logger.warn(`\tYou can only use literal RPC function names, without \${...} substitutions`);
177
234
  }
178
235
  }
179
236
  }
@@ -0,0 +1,26 @@
1
+ import * as ts from 'typescript';
2
+ export type StartedWorkflows = {
3
+ /** Workflow names passed as a literal to `rpc.startWorkflow(...)`. */
4
+ names: string[];
5
+ /** Source text of `rpc.startWorkflow(...)` calls whose name is computed. */
6
+ dynamic: string[];
7
+ /**
8
+ * Plain functions the handler passes `rpc` to — calls made inside them are
9
+ * not seen. Methods (`workflowService.runToCompletion(..., rpc)`) are left
10
+ * out: they are services, not the app's own helpers.
11
+ */
12
+ rpcHandoffs: string[];
13
+ };
14
+ /**
15
+ * The workflows a function handler starts through `rpc.startWorkflow(...)`,
16
+ * read from its own body.
17
+ *
18
+ * `startWorkflow` looks the workflow up by name in the process that calls it,
19
+ * so a deployed unit that starts a workflow needs that workflow's meta bundled
20
+ * in. The deploy planner does that from these names.
21
+ *
22
+ * Only the handler's own body is read. When the handler passes `rpc` on to a
23
+ * helper, whatever the helper starts is invisible here; those callees are
24
+ * returned in `rpcHandoffs` so the caller can say so.
25
+ */
26
+ export declare function collectStartedWorkflows(handler: ts.Node): StartedWorkflows;
@@ -0,0 +1,72 @@
1
+ import * as ts from 'typescript';
2
+ /** `x` seen through `x!` and `(x)`. */
3
+ function unwrap(node) {
4
+ let inner = node;
5
+ while (ts.isNonNullExpression(inner) || ts.isParenthesizedExpression(inner)) {
6
+ inner = inner.expression;
7
+ }
8
+ return inner;
9
+ }
10
+ /** `rpc`, or anything ending in `.rpc` (`wire.rpc`), seen through `!` and parens. */
11
+ function isRpcExpression(node) {
12
+ const inner = unwrap(node);
13
+ return ts.isIdentifier(inner)
14
+ ? inner.text === 'rpc'
15
+ : ts.isPropertyAccessExpression(inner) && inner.name.text === 'rpc';
16
+ }
17
+ /** An argument that hands `rpc` over: `rpc`, `wire.rpc`, `{ rpc }`, `{ rpc: wire.rpc }`. */
18
+ function passesRpc(arg) {
19
+ if (isRpcExpression(arg))
20
+ return true;
21
+ const inner = unwrap(arg);
22
+ if (!ts.isObjectLiteralExpression(inner))
23
+ return false;
24
+ return inner.properties.some((p) => (ts.isShorthandPropertyAssignment(p) && p.name.text === 'rpc') ||
25
+ (ts.isPropertyAssignment(p) && isRpcExpression(p.initializer)));
26
+ }
27
+ /**
28
+ * The workflows a function handler starts through `rpc.startWorkflow(...)`,
29
+ * read from its own body.
30
+ *
31
+ * `startWorkflow` looks the workflow up by name in the process that calls it,
32
+ * so a deployed unit that starts a workflow needs that workflow's meta bundled
33
+ * in. The deploy planner does that from these names.
34
+ *
35
+ * Only the handler's own body is read. When the handler passes `rpc` on to a
36
+ * helper, whatever the helper starts is invisible here; those callees are
37
+ * returned in `rpcHandoffs` so the caller can say so.
38
+ */
39
+ export function collectStartedWorkflows(handler) {
40
+ const names = new Set();
41
+ const dynamic = [];
42
+ const rpcHandoffs = new Set();
43
+ const visit = (node) => {
44
+ if (ts.isCallExpression(node)) {
45
+ const callee = node.expression;
46
+ const isRpcMethod = ts.isPropertyAccessExpression(callee) &&
47
+ isRpcExpression(callee.expression);
48
+ if (isRpcMethod && callee.name.text === 'startWorkflow') {
49
+ const [firstArg] = node.arguments;
50
+ if (firstArg) {
51
+ if (ts.isStringLiteral(firstArg) ||
52
+ ts.isNoSubstitutionTemplateLiteral(firstArg)) {
53
+ names.add(firstArg.text);
54
+ }
55
+ else {
56
+ dynamic.push(node.getText());
57
+ }
58
+ }
59
+ }
60
+ else if (ts.isIdentifier(callee) && node.arguments.some(passesRpc)) {
61
+ rpcHandoffs.add(callee.text);
62
+ }
63
+ }
64
+ ts.forEachChild(node, visit);
65
+ };
66
+ visit(handler);
67
+ return {
68
+ names: [...names].sort(),
69
+ dynamic,
70
+ rpcHandoffs: [...rpcHandoffs].sort(),
71
+ };
72
+ }
package/dist/types.d.ts CHANGED
@@ -227,6 +227,7 @@ export type InspectorFilters = {
227
227
  excludeTarget?: Array<'serverless' | 'server'>;
228
228
  serverlessIncompatible?: string[];
229
229
  defaultTarget?: 'serverless' | 'server';
230
+ workflowMeta?: string[];
230
231
  };
231
232
  export type AddonConfig = {
232
233
  package: string;
@@ -831,6 +831,54 @@ export function filterInspectorState(state, filters, logger) {
831
831
  }
832
832
  }
833
833
  }
834
+ // Workflows asked for by meta only — a unit that `rpc.startWorkflow`s one the
835
+ // orchestrator unit runs. The run is created from the meta and handed to the
836
+ // orchestrator queue, so the meta (and the workflow function's meta, which
837
+ // the generated workflow map types the start from) is restored, while the
838
+ // registration files stay out: bundling them would pull the workflow
839
+ // function and everything it imports into the unit.
840
+ const fullWorkflowNames = new Set(filters.names ?? []);
841
+ for (const name of filters.workflowMeta ?? []) {
842
+ const graphMeta = state.workflows?.graphMeta?.[name];
843
+ const workflowMeta = state.workflows?.meta?.[name];
844
+ if (!graphMeta && !workflowMeta)
845
+ continue;
846
+ const pikkuFuncId = graphMeta?.pikkuFuncId ?? workflowMeta?.pikkuFuncId;
847
+ if (graphMeta && !filteredState.workflows.graphMeta[name]) {
848
+ filteredState.workflows.graphMeta[name] = JSON.parse(JSON.stringify(graphMeta));
849
+ }
850
+ if (workflowMeta && !filteredState.workflows.meta[name]) {
851
+ filteredState.workflows.meta[name] = JSON.parse(JSON.stringify(workflowMeta));
852
+ }
853
+ const funcMeta = pikkuFuncId ? state.functions.meta[pikkuFuncId] : undefined;
854
+ if (pikkuFuncId && funcMeta && !filteredState.functions.meta[pikkuFuncId]) {
855
+ filteredState.functions.meta[pikkuFuncId] = funcMeta;
856
+ }
857
+ // The orchestrator queue's meta routes the start to the workflow's own
858
+ // queue instead of the shared fallback; its consumer is core's synthetic
859
+ // orchestrator, so no registration comes with it.
860
+ const orchestratorQueue = `wf-orchestrator-${name
861
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
862
+ .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
863
+ .toLowerCase()}`;
864
+ const orchestratorMeta = state.queueWorkers?.meta?.[orchestratorQueue];
865
+ if (orchestratorMeta &&
866
+ !filteredState.queueWorkers.meta[orchestratorQueue]) {
867
+ filteredState.queueWorkers.meta[orchestratorQueue] = JSON.parse(JSON.stringify(orchestratorMeta));
868
+ }
869
+ const keepsRegistration = fullWorkflowNames.has(name) ||
870
+ (pikkuFuncId !== undefined &&
871
+ filteredState.serviceAggregation.usedFunctions.has(pikkuFuncId));
872
+ if (!keepsRegistration) {
873
+ for (const key of [name, pikkuFuncId]) {
874
+ if (!key)
875
+ continue;
876
+ filteredState.workflows.files.delete(key);
877
+ filteredState.workflows.graphFiles.delete(key);
878
+ }
879
+ }
880
+ filteredState.serviceAggregation.requiredServices.add('workflowService');
881
+ }
834
882
  // Recompute requiredSchemas based on pruned functions.meta
835
883
  if (filteredState.serviceAggregation.usedFunctions.size > 0) {
836
884
  const prunedSchemas = new Set();
@@ -1,5 +1,13 @@
1
1
  import type { InspectorState, InspectorLogger } from '../types.js';
2
+ import type { WebhookSourcesMeta } from '@pikku/core/trigger';
2
3
  export declare const describeMissingAddonMeta: (namespace: string, packageName: string, dirs: string[], declFile?: string) => string;
4
+ /**
5
+ * The webhook sources an addon declares, as the app mounts them. The source is
6
+ * named after the addon's namespace, so two instances of one addon get a
7
+ * route each; an addon declaring several suffixes each with its own name.
8
+ * Every source starts off: the app turns one on at runtime.
9
+ */
10
+ export declare const namespaceAddonWebhookSources: (sources: WebhookSourcesMeta, namespace: string) => WebhookSourcesMeta;
3
11
  /**
4
12
  * After the setup sweep discovers wireAddon() declarations, load each addon
5
13
  * package's function metadata so that wiring handlers (channels, HTTP routes,
@@ -151,6 +151,31 @@ const registerAddonTypes = (state, metaPath, meta) => {
151
151
  }
152
152
  }
153
153
  };
154
+ /**
155
+ * The webhook sources an addon declares, as the app mounts them. The source is
156
+ * named after the addon's namespace, so two instances of one addon get a
157
+ * route each; an addon declaring several suffixes each with its own name.
158
+ * Every source starts off: the app turns one on at runtime.
159
+ */
160
+ export const namespaceAddonWebhookSources = (sources, namespace) => {
161
+ const declared = Object.values(sources);
162
+ const namespaced = {};
163
+ for (const source of declared) {
164
+ const name = declared.length === 1 ? namespace : `${namespace}-${source.name}`;
165
+ const meta = {
166
+ ...source,
167
+ name,
168
+ route: `/webhooks/${name}`,
169
+ };
170
+ for (const step of ['receive', 'check', 'setup', 'teardown']) {
171
+ const funcId = source[step];
172
+ if (funcId && !funcId.includes(':'))
173
+ meta[step] = `${namespace}:${funcId}`;
174
+ }
175
+ namespaced[name] = meta;
176
+ }
177
+ return namespaced;
178
+ };
154
179
  /**
155
180
  * After the setup sweep discovers wireAddon() declarations, load each addon
156
181
  * package's function metadata so that wiring handlers (channels, HTTP routes,
@@ -366,6 +391,28 @@ export async function loadAddonFunctionsMeta(logger, state) {
366
391
  }
367
392
  catch { }
368
393
  }
394
+ try {
395
+ const webhookSourcesPath = require.resolve(`${decl.package}/.pikku/webhooks/pikku-webhook-sources-meta.gen.json`);
396
+ const sources = namespaceAddonWebhookSources(JSON.parse(await readFile(webhookSourcesPath, 'utf-8')), namespace);
397
+ for (const [name, source] of Object.entries(sources)) {
398
+ if (state.triggers.webhookSourceMeta[name])
399
+ continue;
400
+ state.triggers.webhookSourceMeta[name] = source;
401
+ for (const step of [
402
+ 'receive',
403
+ 'check',
404
+ 'setup',
405
+ 'teardown',
406
+ ]) {
407
+ const funcId = source[step];
408
+ if (funcId)
409
+ state.serviceAggregation.usedFunctions.add(funcId);
410
+ }
411
+ }
412
+ }
413
+ catch {
414
+ // No addon webhook sources
415
+ }
369
416
  try {
370
417
  const httpContractsPath = require.resolve(`${decl.package}/.pikku/http/pikku-http-contracts-meta.gen.json`);
371
418
  const httpContractsRaw = await readFile(httpContractsPath, 'utf-8');
@@ -1,7 +1,7 @@
1
- import type { InspectorState, InspectorLogger, InspectorOptions } from '../types.js';
2
- import type { PermissionMetadata } from '@pikku/core/function';
3
- import type { MiddlewareMetadata } from '@pikku/core/middleware';
4
- import type ts from 'typescript';
1
+ import type { InspectorState, InspectorLogger, InspectorOptions } from "../types.js";
2
+ import type { PermissionMetadata } from "@pikku/core/function";
3
+ import type { MiddlewareMetadata } from "@pikku/core/middleware";
4
+ import type ts from "typescript";
5
5
  /**
6
6
  * Stamp the inspected authorize/callbacks service set onto the generated auth
7
7
  * handler's function meta.
@@ -15,7 +15,7 @@ import type ts from 'typescript';
15
15
  * onto the handler meta. Re-derived every inspect and ordered BEFORE
16
16
  * `aggregateRequiredServices` so it flows into `requiredServices`.
17
17
  */
18
- export declare function stampAuthHandlerServices(state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
18
+ export declare function stampAuthHandlerServices(state: InspectorState | Omit<InspectorState, "typesLookup">): void;
19
19
  /**
20
20
  * Helper to extract wire-level middleware/permission names from metadata.
21
21
  * Only extracts type:'wire' variants (individual middleware/permissions).
@@ -29,10 +29,10 @@ export declare function extractWireNames(list?: Array<MiddlewareMetadata | Permi
29
29
  * Note: usedFunctions, usedMiddleware, and usedPermissions are tracked directly
30
30
  * in the add-* methods during AST traversal for efficiency.
31
31
  */
32
- export declare function aggregateRequiredServices(state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
33
- export declare function validateSecretOverrides(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
34
- export declare function validateCredentialOverrides(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
35
- export declare function validateVariableOverrides(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
32
+ export declare function aggregateRequiredServices(state: InspectorState | Omit<InspectorState, "typesLookup">): void;
33
+ export declare function validateSecretOverrides(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, "typesLookup">): void;
34
+ export declare function validateCredentialOverrides(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, "typesLookup">): void;
35
+ export declare function validateVariableOverrides(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, "typesLookup">): void;
36
36
  /**
37
37
  * Every wired addon must be installed, in the bucket matching how it is used.
38
38
  *
@@ -56,14 +56,14 @@ export declare function validateVariableOverrides(logger: InspectorLogger, state
56
56
  * up in `dependencies`. Asking the resolver is the only question that matches
57
57
  * what the loader will actually do.
58
58
  */
59
- export declare function validateRemoteAddonDependencies(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
59
+ export declare function validateRemoteAddonDependencies(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, "typesLookup">): void;
60
60
  /**
61
61
  * The auth a `wireRemoteAddon` consumer binds must reference a slot the consumer
62
62
  * actually declares: a `credentialId` must be a wired credential, a `secretId`
63
63
  * a wired secret. A custom `resolve()` and a public (omitted) surface are not
64
64
  * statically checkable and are left to runtime.
65
65
  */
66
- export declare function validateRemoteAddonAuth(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
66
+ export declare function validateRemoteAddonAuth(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, "typesLookup">): void;
67
67
  export declare function computeResolvedIOTypes(state: InspectorState): void;
68
68
  export declare function computeMiddlewareGroupsMeta(state: InspectorState): void;
69
69
  export declare function computePermissionsGroupsMeta(state: InspectorState): void;
@@ -88,7 +88,7 @@ export declare function validateSchemaReferences(logger: InspectorLogger, state:
88
88
  * A model is either concrete (`provider/model`) or an alias, which must appear
89
89
  * in the `models` table from pikku.config.json.
90
90
  */
91
- export declare function validateAgentModels(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>, modelAliases?: Record<string, string>): void;
91
+ export declare function validateAgentModels(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, "typesLookup">, modelAliases?: Record<string, string>): void;
92
92
  /**
93
93
  * Scenarios are pure stories of remote RPCs (same rule as client-side CLI
94
94
  * renderers): the func may only destructure logger/config — everything else
@@ -100,7 +100,7 @@ export declare function validateAgentModels(logger: InspectorLogger, state: Insp
100
100
  * `addFunctions` (`visitFunctions`) has populated `state.functions.meta` — so
101
101
  * the check read `undefined` there and never fired.
102
102
  */
103
- export declare function validateScenarioServices(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
103
+ export declare function validateScenarioServices(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, "typesLookup">): void;
104
104
  /**
105
105
  * Does this step's prose open by naming the actor who is already its subject?
106
106
  *
@@ -128,7 +128,7 @@ export declare const proseOpensWithActor: (prose: string, actor: string) => bool
128
128
  * It also marks the step's function as used, so `scenario run` boots the
129
129
  * singletons the step actually needs.
130
130
  */
131
- export declare function validateScenarioSteps(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
131
+ export declare function validateScenarioSteps(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, "typesLookup">): void;
132
132
  /**
133
133
  * Reports scenarios that no `pikkuFeature` lists.
134
134
  *
@@ -153,7 +153,7 @@ export declare function validateScenarioSteps(logger: InspectorLogger, state: In
153
153
  * the check reports it then and downgrades to a warn naming both sides, rather
154
154
  * than accusing a scenario that may well be in that array.
155
155
  */
156
- export declare function validateScenarioFeatures(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
156
+ export declare function validateScenarioFeatures(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, "typesLookup">): void;
157
157
  /**
158
158
  * A `pikkuWorkflowGraph` node that references the `graph:` namespace (e.g.
159
159
  * `graph:editFields`) needs @pikku/addon-graph wired — otherwise the RPC never
@@ -178,7 +178,7 @@ export declare function validateSchemaWiringSeparation(logger: InspectorLogger,
178
178
  * a name the omit does not cover. A name-based rule misses this; the type does
179
179
  * not.
180
180
  */
181
- export declare function validateNoSecretAliasServices(logger: InspectorLogger, checker: ts.TypeChecker, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
181
+ export declare function validateNoSecretAliasServices(logger: InspectorLogger, checker: ts.TypeChecker, state: InspectorState | Omit<InspectorState, "typesLookup">): void;
182
182
  /**
183
183
  * Reports secret reads the catalogue does not account for.
184
184
  *
@@ -191,8 +191,8 @@ export declare function validateNoSecretAliasServices(logger: InspectorLogger, c
191
191
  * deployment is asked for the client id and secret every connect flow needs
192
192
  * without an author restating a shape the runtime already fixes.
193
193
  */
194
- export declare function registerDerivedOAuth2AppSecrets(state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
195
- export declare function validateSecretUsage(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
194
+ export declare function registerDerivedOAuth2AppSecrets(state: InspectorState | Omit<InspectorState, "typesLookup">): void;
195
+ export declare function validateSecretUsage(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, "typesLookup">): void;
196
196
  export declare function computeDiagnostics(state: InspectorState): void;
197
197
  /**
198
198
  * Validates that every scope referenced by a function is declared via
@@ -201,7 +201,7 @@ export declare function computeDiagnostics(state: InspectorState): void;
201
201
  * A `*` suffix is a wildcard requirement: `admin:*` requires the `admin` scope
202
202
  * to be declared, and grants its whole subtree.
203
203
  */
204
- export declare function validateScopeReferences(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
204
+ export declare function validateScopeReferences(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, "typesLookup">): void;
205
205
  /**
206
206
  * Resolves every `tools:` entry on every agent and reports the ones that do not
207
207
  * land on a real function, or that land on one with nothing to tell the model.
@@ -218,7 +218,7 @@ export declare function validateScopeReferences(logger: InspectorLogger, state:
218
218
  * quality loss rather than a failure. A title deliberately does not satisfy it —
219
219
  * a title labels a tool in a UI, it does not tell a model when to reach for it.
220
220
  */
221
- export declare function validateAgentToolReferences(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>, options?: InspectorOptions): void;
221
+ export declare function validateAgentToolReferences(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, "typesLookup">, options?: InspectorOptions): void;
222
222
  /**
223
223
  * Validates that every scorer an agent asks to be graded by exists.
224
224
  *
@@ -226,7 +226,7 @@ export declare function validateAgentToolReferences(logger: InspectorLogger, sta
226
226
  * that has already answered the user — which means a typo would otherwise cost
227
227
  * nothing visible and simply grade nothing, forever.
228
228
  */
229
- export declare function validateAgentScorerReferences(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
229
+ export declare function validateAgentScorerReferences(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, "typesLookup">): void;
230
230
  /**
231
231
  * Validates that every scope granted by a system role is declared via
232
232
  * `defineScope`. Runs after all visitors, so declaration order does not matter.
@@ -239,7 +239,7 @@ export declare function validateAgentScorerReferences(logger: InspectorLogger, s
239
239
  *
240
240
  * A `*` suffix grants a subtree; the node it hangs off must exist.
241
241
  */
242
- export declare function validateSystemRoleScopes(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
242
+ export declare function validateSystemRoleScopes(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, "typesLookup">): void;
243
243
  /**
244
244
  * Validates that every role a persona names is declared with
245
245
  * `defineSystemRole`. Runs after all visitors, so declaration order does not
@@ -251,4 +251,4 @@ export declare function validateSystemRoleScopes(logger: InspectorLogger, state:
251
251
  * symptom is a scatter of 403s that read like authorization findings rather
252
252
  * than like a deleted role.
253
253
  */
254
- export declare function validatePersonaRoles(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
254
+ export declare function validatePersonaRoles(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, "typesLookup">): void;