@pikku/inspector 0.12.75 → 0.12.82
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 +357 -0
- package/dist/add/add-analytics.d.ts +9 -0
- package/dist/add/add-analytics.js +178 -0
- package/dist/add/add-auth.js +0 -54
- package/dist/add/add-feature-flag.d.ts +2 -0
- package/dist/add/add-feature-flag.js +162 -0
- package/dist/add/add-middleware.js +66 -42
- package/dist/add/add-wire-addon.js +7 -4
- package/dist/add/add-wire-remote-addon.js +1 -0
- package/dist/error-codes.d.ts +1 -0
- package/dist/error-codes.js +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/inspector.js +5 -0
- package/dist/surface/collect-surface.d.ts +58 -0
- package/dist/surface/collect-surface.js +613 -0
- package/dist/types.d.ts +36 -12
- package/dist/utils/filter-inspector-state.js +14 -0
- package/dist/utils/load-addon-functions-meta.js +24 -11
- package/dist/utils/post-process.js +15 -0
- package/dist/utils/read-module-specifiers.d.ts +8 -0
- package/dist/utils/read-module-specifiers.js +23 -0
- package/dist/utils/read-tsconfig-out-dir.d.ts +5 -0
- package/dist/utils/read-tsconfig-out-dir.js +12 -0
- package/dist/utils/resolve-deploy-target.d.ts +7 -0
- package/dist/utils/resolve-deploy-target.js +15 -7
- package/dist/utils/serialize-inspector-state.d.ts +6 -0
- package/dist/utils/serialize-inspector-state.js +10 -0
- package/dist/visit.js +4 -0
- package/package.json +16 -6
- package/run-tests.sh +21 -6
- package/src/add/add-addon-bans.ts +0 -85
- package/src/add/add-agent-scorer.test.ts +0 -197
- package/src/add/add-agent-scorer.ts +0 -83
- package/src/add/add-agent.test.ts +0 -87
- package/src/add/add-agent.ts +0 -638
- package/src/add/add-approval-description.ts +0 -76
- package/src/add/add-auth.test.ts +0 -791
- package/src/add/add-auth.ts +0 -424
- package/src/add/add-channel.ts +0 -778
- package/src/add/add-cli-renderers.test.ts +0 -74
- package/src/add/add-cli.ts +0 -1006
- package/src/add/add-credential.ts +0 -186
- package/src/add/add-feature.test.ts +0 -306
- package/src/add/add-feature.ts +0 -253
- package/src/add/add-file-extends-core-type.ts +0 -66
- package/src/add/add-file-with-config.ts +0 -45
- package/src/add/add-file-with-factory.test.ts +0 -134
- package/src/add/add-file-with-factory.ts +0 -288
- package/src/add/add-functions.test.ts +0 -743
- package/src/add/add-functions.ts +0 -1548
- package/src/add/add-gateway.ts +0 -143
- package/src/add/add-http-route-ref.test.ts +0 -149
- package/src/add/add-http-route.ts +0 -530
- package/src/add/add-http-routes.ts +0 -471
- package/src/add/add-keyed-wiring.ts +0 -180
- package/src/add/add-mcp-prompt.ts +0 -157
- package/src/add/add-mcp-resource.ts +0 -175
- package/src/add/add-middleware.test.ts +0 -196
- package/src/add/add-middleware.ts +0 -879
- package/src/add/add-permission-auth.test.ts +0 -59
- package/src/add/add-permission.ts +0 -311
- package/src/add/add-personas.test.ts +0 -406
- package/src/add/add-personas.ts +0 -287
- package/src/add/add-queue-worker.ts +0 -117
- package/src/add/add-rpc-invocations.test.ts +0 -287
- package/src/add/add-rpc-invocations.ts +0 -221
- package/src/add/add-scenario-step.test.ts +0 -509
- package/src/add/add-schedule.ts +0 -120
- package/src/add/add-scope.test.ts +0 -419
- package/src/add/add-scope.ts +0 -221
- package/src/add/add-secret.ts +0 -10
- package/src/add/add-subject-scenario-steps.test.ts +0 -161
- package/src/add/add-system-role.test.ts +0 -297
- package/src/add/add-system-role.ts +0 -162
- package/src/add/add-trigger.ts +0 -165
- package/src/add/add-variable.ts +0 -10
- package/src/add/add-wire-addon.test.ts +0 -185
- package/src/add/add-wire-addon.ts +0 -146
- package/src/add/add-wire-remote-addon.ts +0 -80
- package/src/add/add-workflow-edge-cases.test.ts +0 -201
- package/src/add/add-workflow-fanout.test.ts +0 -166
- package/src/add/add-workflow-graph-foreach.test.ts +0 -174
- package/src/add/add-workflow-graph-input.test.ts +0 -94
- package/src/add/add-workflow-graph-notes.test.ts +0 -74
- package/src/add/add-workflow-graph.ts +0 -644
- package/src/add/add-workflow-on-error.test.ts +0 -118
- package/src/add/add-workflow-return-switch.test.ts +0 -121
- package/src/add/add-workflow-silent-drops.test.ts +0 -261
- package/src/add/add-workflow-sleep-expression.test.ts +0 -96
- package/src/add/add-workflow.test.ts +0 -155
- package/src/add/add-workflow.ts +0 -482
- package/src/add/addon-bans.test.ts +0 -121
- package/src/add/addon-contracts.test.ts +0 -221
- package/src/add/allow-escape-hatches.test.ts +0 -144
- package/src/add/audit-meta.test.ts +0 -75
- package/src/add/classified-sink-check.test.ts +0 -347
- package/src/add/config-value-metadata.test.ts +0 -263
- package/src/add/dynamic-import-check.test.ts +0 -119
- package/src/add/expect-eventually-scenario-only.test.ts +0 -108
- package/src/add/function-body-span.test.ts +0 -96
- package/src/add/inline-wiring-function.test.ts +0 -104
- package/src/add/pii-check.test.ts +0 -258
- package/src/add/rpc-type-cast.test.ts +0 -123
- package/src/add/scenario-instrumentation.test.ts +0 -89
- package/src/add/scenario-instrumentation.ts +0 -42
- package/src/add/scenario-marker.test.ts +0 -89
- package/src/add/scenario-not-extractable.test.ts +0 -159
- package/src/add/scenario-services.test.ts +0 -103
- package/src/add/scenario-skip.test.ts +0 -83
- package/src/add/validate-workflow-graph-addons.test.ts +0 -102
- package/src/add/wire-name-literal.test.ts +0 -117
- package/src/error-codes-emitted.test.ts +0 -76
- package/src/error-codes.ts +0 -145
- package/src/index.ts +0 -66
- package/src/inspector.ts +0 -575
- package/src/schema-generator.ts +0 -1
- package/src/source-file-cache.ts +0 -73
- package/src/tsconfig-paths.test.ts +0 -90
- package/src/types-map.ts +0 -141
- package/src/types.ts +0 -775
- package/src/utils/annotate-http-route-auth.test.ts +0 -161
- package/src/utils/annotate-http-route-auth.ts +0 -53
- package/src/utils/check-classified-sinks.ts +0 -215
- package/src/utils/check-pii-output.ts +0 -120
- package/src/utils/compute-diagnostics.test.ts +0 -106
- package/src/utils/compute-required-schemas.ts +0 -48
- package/src/utils/contract-hashes.test.ts +0 -553
- package/src/utils/contract-hashes.ts +0 -387
- package/src/utils/custom-types-generator.test.ts +0 -124
- package/src/utils/custom-types-generator.ts +0 -150
- package/src/utils/detect-schema-vendor.test.ts +0 -123
- package/src/utils/detect-schema-vendor.ts +0 -120
- package/src/utils/does-type-extend-core-type.ts +0 -53
- package/src/utils/ensure-function-metadata.ts +0 -385
- package/src/utils/extract-function-name.ts +0 -612
- package/src/utils/extract-function-node.ts +0 -70
- package/src/utils/extract-node-value.test.ts +0 -117
- package/src/utils/extract-node-value.ts +0 -172
- package/src/utils/extract-secret-usage.test.ts +0 -81
- package/src/utils/extract-secret-usage.ts +0 -73
- package/src/utils/extract-services.ts +0 -70
- package/src/utils/extract-surface-usage.test.ts +0 -147
- package/src/utils/extract-surface-usage.ts +0 -121
- package/src/utils/filter-inspector-state.test.ts +0 -2232
- package/src/utils/filter-inspector-state.ts +0 -1298
- package/src/utils/filter-utils.test.ts +0 -843
- package/src/utils/filter-utils.ts +0 -161
- package/src/utils/find-root-dir.ts +0 -68
- package/src/utils/find-zod-transform.test.ts +0 -70
- package/src/utils/find-zod-transform.ts +0 -103
- package/src/utils/get-exported-variable-name.ts +0 -48
- package/src/utils/get-files-and-methods.ts +0 -157
- package/src/utils/get-property-value.test.ts +0 -141
- package/src/utils/get-property-value.ts +0 -331
- package/src/utils/hash.ts +0 -26
- package/src/utils/literal-properties.ts +0 -97
- package/src/utils/load-addon-functions-meta.test.ts +0 -157
- package/src/utils/load-addon-functions-meta.ts +0 -508
- package/src/utils/load-addon-scopes.test.ts +0 -138
- package/src/utils/middleware.test.ts +0 -204
- package/src/utils/middleware.ts +0 -303
- package/src/utils/nested-project-filter.test.ts +0 -53
- package/src/utils/nested-project-filter.ts +0 -28
- package/src/utils/permissions-shorthand.test.ts +0 -79
- package/src/utils/permissions.test.ts +0 -125
- package/src/utils/permissions.ts +0 -129
- package/src/utils/post-process.test.ts +0 -1094
- package/src/utils/post-process.ts +0 -1604
- package/src/utils/resolve-addon-package.ts +0 -106
- package/src/utils/resolve-core-type.test.ts +0 -142
- package/src/utils/resolve-core-type.ts +0 -35
- package/src/utils/resolve-deploy-target.test.ts +0 -135
- package/src/utils/resolve-deploy-target.ts +0 -65
- package/src/utils/resolve-function-meta.ts +0 -25
- package/src/utils/resolve-function-types.ts +0 -42
- package/src/utils/resolve-identifier.ts +0 -46
- package/src/utils/resolve-ref-contract.ts +0 -71
- package/src/utils/resolve-versions.test.ts +0 -499
- package/src/utils/resolve-versions.ts +0 -228
- package/src/utils/schema-cache.test.ts +0 -124
- package/src/utils/schema-generator.test.ts +0 -272
- package/src/utils/schema-generator.ts +0 -780
- package/src/utils/secret-alias-services.test.ts +0 -146
- package/src/utils/secret-alias-services.ts +0 -58
- package/src/utils/serialize-inspector-state.ts +0 -742
- package/src/utils/serialize-mcp-json.ts +0 -150
- package/src/utils/serialize-middleware-groups-meta.ts +0 -36
- package/src/utils/serialize-openapi-json.ts +0 -277
- package/src/utils/serialize-permissions-groups-meta.ts +0 -7
- package/src/utils/single-declaration.ts +0 -57
- package/src/utils/type-utils.ts +0 -145
- package/src/utils/validate-auth-sessionless.ts +0 -30
- package/src/utils/validate-exposed-functions-gated.test.ts +0 -230
- package/src/utils/validate-exposed-functions-gated.ts +0 -103
- package/src/utils/validate-tags-resolve-to-middleware.test.ts +0 -108
- package/src/utils/validate-tags-resolve-to-middleware.ts +0 -48
- package/src/utils/workflow/derive-workflow-plan.test.ts +0 -178
- package/src/utils/workflow/derive-workflow-plan.ts +0 -117
- package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +0 -1465
- package/src/utils/workflow/dsl/deserialize-fanout-body.test.ts +0 -71
- package/src/utils/workflow/dsl/deserialize-fanout-fidelity.test.ts +0 -99
- package/src/utils/workflow/dsl/deserialize-graph-next.test.ts +0 -161
- package/src/utils/workflow/dsl/deserialize-roundtrip-bindings.test.ts +0 -121
- package/src/utils/workflow/dsl/deserialize-roundtrip-values.test.ts +0 -116
- package/src/utils/workflow/dsl/deserialize-roundtrip.test.ts +0 -145
- package/src/utils/workflow/dsl/extract-approval-policy.test.ts +0 -71
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +0 -2254
- package/src/utils/workflow/dsl/index.ts +0 -11
- package/src/utils/workflow/dsl/patterns.ts +0 -440
- package/src/utils/workflow/dsl/validation.ts +0 -203
- package/src/utils/workflow/graph/convert-dsl-to-graph-skip.test.ts +0 -35
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +0 -543
- package/src/utils/workflow/graph/fanout-node-identity.test.ts +0 -105
- package/src/utils/workflow/graph/finalize-workflow-wires.ts +0 -310
- package/src/utils/workflow/graph/finalize-workflows.test.ts +0 -50
- package/src/utils/workflow/graph/finalize-workflows.ts +0 -130
- package/src/utils/workflow/graph/index.ts +0 -28
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +0 -240
- package/src/utils/workflow/graph/workflow-graph.types.ts +0 -281
- package/src/visit.ts +0 -181
- package/tsconfig.json +0 -19
- package/tsconfig.tsbuildinfo +0 -1
package/dist/types.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ import type { SecretDefinitions } from '@pikku/core/secret';
|
|
|
18
18
|
import type { CredentialDefinitions } from '@pikku/core/credential';
|
|
19
19
|
import type { ScopeDefinitions } from '@pikku/core/scope';
|
|
20
20
|
import type { SystemRoleDefinitions } from '@pikku/core/role';
|
|
21
|
+
import type { FeatureFlagDefinitions } from '@pikku/core/flag';
|
|
21
22
|
import type { PersonaDefinitions } from '@pikku/core/persona';
|
|
22
23
|
import type { VariableDefinitions } from '@pikku/core/variable';
|
|
23
24
|
import type { TypesMap } from './types-map.js';
|
|
@@ -161,6 +162,16 @@ export interface InspectorMiddlewareState {
|
|
|
161
162
|
definitions: Record<string, InspectorMiddlewareDefinition>;
|
|
162
163
|
instances: Record<string, InspectorMiddlewareInstance>;
|
|
163
164
|
tagMiddleware: Map<string, MiddlewareGroupMeta>;
|
|
165
|
+
/**
|
|
166
|
+
* Every source file with a top-level `addGlobalMiddleware` call.
|
|
167
|
+
*
|
|
168
|
+
* Global middleware belongs to no wire group, so the only thing that makes it
|
|
169
|
+
* run in a generated bundle is a side-effect import of its source file. This
|
|
170
|
+
* set is the record codegen emits those imports from, and the one it checks
|
|
171
|
+
* itself against — a plain set of file names, so nothing can overwrite an
|
|
172
|
+
* entry the way a keyed map could.
|
|
173
|
+
*/
|
|
174
|
+
globalFiles: Set<string>;
|
|
164
175
|
}
|
|
165
176
|
export interface InspectorChannelMiddlewareState {
|
|
166
177
|
definitions: Record<string, InspectorMiddlewareDefinition>;
|
|
@@ -470,6 +481,19 @@ export interface InspectorState {
|
|
|
470
481
|
addonRequiredParentServices: string[];
|
|
471
482
|
addonCreatedServices: string[];
|
|
472
483
|
addonServicesFactorySeen: boolean;
|
|
484
|
+
/**
|
|
485
|
+
* The project's `defineAnalyticsEvents` declarations, in visit order. Absent
|
|
486
|
+
* when nothing declares any.
|
|
487
|
+
*/
|
|
488
|
+
analytics?: Array<{
|
|
489
|
+
file: string;
|
|
490
|
+
variable: string;
|
|
491
|
+
events: string[];
|
|
492
|
+
/** Each event's props as declared, keyed by event name then prop name, with
|
|
493
|
+
* the schema's own source text as the value. Absent for a shape the
|
|
494
|
+
* inspector cannot read off the declaration. */
|
|
495
|
+
props?: Record<string, Record<string, string>>;
|
|
496
|
+
}>;
|
|
473
497
|
addonServerlessIncompatible: Map<string, string[]>;
|
|
474
498
|
configFactories: PathToNameAndType;
|
|
475
499
|
serverLifecycleFactories: PathToNameAndType;
|
|
@@ -548,8 +572,15 @@ export interface InspectorState {
|
|
|
548
572
|
usedAddons: Set<string>;
|
|
549
573
|
wireAddonDeclarations: Map<string, {
|
|
550
574
|
package: string;
|
|
575
|
+
/** The app source file whose `wireAddon` call declared this instance. */
|
|
576
|
+
file?: string;
|
|
551
577
|
rpcEndpoint?: string;
|
|
552
|
-
|
|
578
|
+
/**
|
|
579
|
+
* `true` offers every function the addon declared `mcp: true`; a list
|
|
580
|
+
* names the functions to offer, whatever the addon declared. Absent
|
|
581
|
+
* when the declared value was not a statically-knowable literal.
|
|
582
|
+
*/
|
|
583
|
+
mcp?: boolean | string[];
|
|
553
584
|
/**
|
|
554
585
|
* The addon's own gates. `runPikkuFunc` applies these to every function
|
|
555
586
|
* in the package, on every wiring path — not just `namespace:function`.
|
|
@@ -633,17 +664,6 @@ export interface InspectorState {
|
|
|
633
664
|
* codebase, if any. The CLI generates the `/auth/*` HTTP wiring from it.
|
|
634
665
|
* More than one `pikkuBetterAuth` is a critical error. */
|
|
635
666
|
definition: AuthDefinition | null;
|
|
636
|
-
/** True when a user (non-generated) file already registers
|
|
637
|
-
* `betterAuthStatelessSession(...)`. The CLI then skips auto-generating its
|
|
638
|
-
* own default-map stateless middleware, which would otherwise pre-empt the
|
|
639
|
-
* user's custom mapSession (pikkujs/pikku#754). */
|
|
640
|
-
userStatelessSession?: boolean;
|
|
641
|
-
/** True when a user (non-generated) file already registers a global
|
|
642
|
-
* `betterAuthSession(...)`. The CLI then skips auto-generating its own
|
|
643
|
-
* default stateful middleware, which would otherwise run first and pre-empt
|
|
644
|
-
* the user's config (mapSession/impersonation/apiKey). Stateful analogue of
|
|
645
|
-
* `userStatelessSession`. */
|
|
646
|
-
hasUserSessionMiddleware?: boolean;
|
|
647
667
|
};
|
|
648
668
|
secrets: {
|
|
649
669
|
definitions: SecretDefinitions;
|
|
@@ -665,6 +685,10 @@ export interface InspectorState {
|
|
|
665
685
|
definitions: SystemRoleDefinitions;
|
|
666
686
|
files: Set<string>;
|
|
667
687
|
};
|
|
688
|
+
featureFlags: {
|
|
689
|
+
definitions: FeatureFlagDefinitions;
|
|
690
|
+
files: Set<string>;
|
|
691
|
+
};
|
|
668
692
|
personas: {
|
|
669
693
|
definitions: PersonaDefinitions;
|
|
670
694
|
files: Set<string>;
|
|
@@ -935,6 +935,20 @@ export function filterInspectorState(state, filters, logger) {
|
|
|
935
935
|
}
|
|
936
936
|
filteredState.rpc.wireAddonDeclarations = new Map([...filteredState.rpc.wireAddonDeclarations].filter(([namespace]) => keptNamespaces.has(namespace)));
|
|
937
937
|
filteredState.rpc.usedAddons = new Set([...filteredState.rpc.usedAddons].filter((namespace) => keptNamespaces.has(namespace)));
|
|
938
|
+
const keptAddonFiles = new Set();
|
|
939
|
+
for (const decl of filteredState.rpc.wireAddonDeclarations.values()) {
|
|
940
|
+
if (decl.file)
|
|
941
|
+
keptAddonFiles.add(decl.file);
|
|
942
|
+
}
|
|
943
|
+
const droppedAddonFiles = new Set();
|
|
944
|
+
for (const [namespace, decl] of state.rpc.wireAddonDeclarations) {
|
|
945
|
+
if (keptNamespaces.has(namespace))
|
|
946
|
+
continue;
|
|
947
|
+
if (decl.file && !keptAddonFiles.has(decl.file)) {
|
|
948
|
+
droppedAddonFiles.add(decl.file);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
filteredState.rpc.wireAddonFiles = new Set([...(filteredState.rpc.wireAddonFiles ?? [])].filter((file) => !droppedAddonFiles.has(file)));
|
|
938
952
|
}
|
|
939
953
|
// Recalculate requiredServices based on filtered functions/middleware/permissions
|
|
940
954
|
// Need to cast to InspectorState temporarily for aggregateRequiredServices
|
|
@@ -2,6 +2,7 @@ import { existsSync } from 'fs';
|
|
|
2
2
|
import { readFile, readdir } from 'fs/promises';
|
|
3
3
|
import { createRequire } from 'module';
|
|
4
4
|
import { join, dirname } from 'path';
|
|
5
|
+
import { ErrorCode } from '../error-codes.js';
|
|
5
6
|
const isHTTPRouteConfig = (value) => typeof value === 'object' &&
|
|
6
7
|
value !== null &&
|
|
7
8
|
'method' in value &&
|
|
@@ -147,19 +148,31 @@ export async function loadAddonFunctionsMeta(logger, state) {
|
|
|
147
148
|
state.addonFunctions[namespace] = meta;
|
|
148
149
|
logger.debug(`Loaded ${Object.keys(meta).length} addon functions for '${namespace}' from ${decl.package}`);
|
|
149
150
|
registerAddonTypes(state, metaPath, meta);
|
|
150
|
-
//
|
|
151
|
+
// `mcp: true` offers what the addon declared as tools; `mcp: [...]` names
|
|
152
|
+
// the functions to offer, whatever the addon declared — the consuming app
|
|
153
|
+
// decides which of the addon's functions a model gets to see.
|
|
151
154
|
if (decl.mcp) {
|
|
155
|
+
const selected = Array.isArray(decl.mcp) ? new Set(decl.mcp) : null;
|
|
152
156
|
for (const [funcName, funcMeta] of Object.entries(meta)) {
|
|
153
|
-
if (funcMeta.mcp) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
157
|
+
if (selected ? !selected.has(funcName) : !funcMeta.mcp) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const toolName = `${namespace}:${funcName}`;
|
|
161
|
+
state.mcpEndpoints.toolsMeta[toolName] = {
|
|
162
|
+
pikkuFuncId: `${namespace}:${funcName}`,
|
|
163
|
+
name: toolName,
|
|
164
|
+
description: funcMeta.description || funcMeta.title || funcName,
|
|
165
|
+
inputSchema: funcMeta.inputSchemaName ?? null,
|
|
166
|
+
outputSchema: funcMeta.outputSchemaName ?? null,
|
|
167
|
+
tags: funcMeta.tags,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
// A name the addon does not publish is a tool the app believes it
|
|
171
|
+
// offers and never does, so it fails the build rather than silently
|
|
172
|
+
// offering one tool fewer than the list says.
|
|
173
|
+
for (const funcName of selected ?? []) {
|
|
174
|
+
if (!(funcName in meta)) {
|
|
175
|
+
logger.critical(ErrorCode.ADDON_MCP_FUNCTION_NOT_FOUND, `wireAddon('${namespace}') lists '${funcName}' under mcp, but ${decl.package} publishes no such function.`);
|
|
163
176
|
}
|
|
164
177
|
}
|
|
165
178
|
}
|
|
@@ -271,6 +271,21 @@ export function aggregateRequiredServices(state) {
|
|
|
271
271
|
if ((state.scopes?.definitions?.length ?? 0) > 0) {
|
|
272
272
|
requiredServices.add('scopeService');
|
|
273
273
|
}
|
|
274
|
+
// Declared feature flags need featureFlags for the same reason, from the
|
|
275
|
+
// other side: `assertFeatureAvailable` reads the source out of the singleton
|
|
276
|
+
// services itself, so a `featureFlag:` on a wiring is the only thing that
|
|
277
|
+
// ever says the project has flags. Without this the flag tables are left out
|
|
278
|
+
// of the generated migration and every gate silently resolves open.
|
|
279
|
+
if ((state.featureFlags?.definitions?.length ?? 0) > 0) {
|
|
280
|
+
requiredServices.add('featureFlags');
|
|
281
|
+
}
|
|
282
|
+
// A `defineAnalyticsEvents` declaration means events are emitted, and the
|
|
283
|
+
// invocation buffer flushes into `analyticsService`. Nothing destructures
|
|
284
|
+
// that either — functions are handed the request-scoped `analytics` — so the
|
|
285
|
+
// declaration is again the only signal.
|
|
286
|
+
if ((state.analytics?.length ?? 0) > 0) {
|
|
287
|
+
requiredServices.add('analyticsService');
|
|
288
|
+
}
|
|
274
289
|
// 7. Services that consumed addons need from the parent project.
|
|
275
290
|
const addonFnServices = new Map();
|
|
276
291
|
for (const [namespace, fns] of Object.entries(state.addonFunctions ?? {})) {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The module specifiers a source file imports from or re-exports from.
|
|
3
|
+
*
|
|
4
|
+
* Parsed rather than grepped: a project's own codegen templates hold the
|
|
5
|
+
* specifiers they emit inside template literals, and those are output text
|
|
6
|
+
* rather than imports the file itself makes.
|
|
7
|
+
*/
|
|
8
|
+
export declare const readModuleSpecifiers: (fileName: string, content: string) => string[];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* The module specifiers a source file imports from or re-exports from.
|
|
4
|
+
*
|
|
5
|
+
* Parsed rather than grepped: a project's own codegen templates hold the
|
|
6
|
+
* specifiers they emit inside template literals, and those are output text
|
|
7
|
+
* rather than imports the file itself makes.
|
|
8
|
+
*/
|
|
9
|
+
export const readModuleSpecifiers = (fileName, content) => {
|
|
10
|
+
const source = ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true);
|
|
11
|
+
const found = [];
|
|
12
|
+
for (const statement of source.statements) {
|
|
13
|
+
if (!ts.isImportDeclaration(statement) &&
|
|
14
|
+
!ts.isExportDeclaration(statement)) {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
const moduleSpecifier = statement.moduleSpecifier;
|
|
18
|
+
if (!moduleSpecifier || !ts.isStringLiteral(moduleSpecifier))
|
|
19
|
+
continue;
|
|
20
|
+
found.push(moduleSpecifier.text);
|
|
21
|
+
}
|
|
22
|
+
return found;
|
|
23
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* A tsconfig's resolved `compilerOptions.outDir`, or undefined when it sets
|
|
4
|
+
* none. Resolved through the compiler so `extends` chains are followed.
|
|
5
|
+
*/
|
|
6
|
+
export const readTsconfigOutDir = (tsconfig) => {
|
|
7
|
+
const parsed = ts.getParsedCommandLineOfConfigFile(tsconfig, {}, {
|
|
8
|
+
...ts.sys,
|
|
9
|
+
onUnRecoverableConfigFileDiagnostic: () => { },
|
|
10
|
+
});
|
|
11
|
+
return parsed?.options.outDir;
|
|
12
|
+
};
|
|
@@ -10,6 +10,13 @@ export declare class IncompatibleDeployTargetError extends Error {
|
|
|
10
10
|
readonly incompatibleServices: string[];
|
|
11
11
|
constructor(functionName: string, incompatibleServices: string[]);
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* The function's services that are declared `serverlessIncompatible`. A
|
|
15
|
+
* non-empty result is exactly the condition that crosses a function to
|
|
16
|
+
* 'server', so callers that need to explain a target — rather than only
|
|
17
|
+
* resolve it — read the reason from here.
|
|
18
|
+
*/
|
|
19
|
+
export declare function incompatibleServicesFor(funcMeta: Pick<FunctionMeta, 'services'>, serverlessIncompatible: Set<string>): string[];
|
|
13
20
|
/**
|
|
14
21
|
* Determine the effective deploy target for a function.
|
|
15
22
|
*
|
|
@@ -17,6 +17,20 @@ export class IncompatibleDeployTargetError extends Error {
|
|
|
17
17
|
this.name = 'IncompatibleDeployTargetError';
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* The function's services that are declared `serverlessIncompatible`. A
|
|
22
|
+
* non-empty result is exactly the condition that crosses a function to
|
|
23
|
+
* 'server', so callers that need to explain a target — rather than only
|
|
24
|
+
* resolve it — read the reason from here.
|
|
25
|
+
*/
|
|
26
|
+
export function incompatibleServicesFor(funcMeta, serverlessIncompatible) {
|
|
27
|
+
const hits = [];
|
|
28
|
+
for (const svc of funcMeta.services?.services ?? []) {
|
|
29
|
+
if (serverlessIncompatible.has(svc))
|
|
30
|
+
hits.push(svc);
|
|
31
|
+
}
|
|
32
|
+
return hits;
|
|
33
|
+
}
|
|
20
34
|
/**
|
|
21
35
|
* Determine the effective deploy target for a function.
|
|
22
36
|
*
|
|
@@ -36,13 +50,7 @@ export class IncompatibleDeployTargetError extends Error {
|
|
|
36
50
|
export function resolveDeployTarget(funcMeta, serverlessIncompatible, functionName = '<unknown>', defaultTarget = 'serverless') {
|
|
37
51
|
// Service compatibility wins over the explicit flag — a serverless
|
|
38
52
|
// bundle of a function that needs (e.g.) node:fs would crash at runtime.
|
|
39
|
-
const incompatibleHits =
|
|
40
|
-
if (funcMeta.services?.services) {
|
|
41
|
-
for (const svc of funcMeta.services.services) {
|
|
42
|
-
if (serverlessIncompatible.has(svc))
|
|
43
|
-
incompatibleHits.push(svc);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
53
|
+
const incompatibleHits = incompatibleServicesFor(funcMeta, serverlessIncompatible);
|
|
46
54
|
if (incompatibleHits.length > 0) {
|
|
47
55
|
if (funcMeta.deploy === 'serverless') {
|
|
48
56
|
throw new IncompatibleDeployTargetError(functionName, incompatibleHits);
|
|
@@ -189,6 +189,7 @@ export interface SerializableInspectorState {
|
|
|
189
189
|
string,
|
|
190
190
|
{
|
|
191
191
|
package: string;
|
|
192
|
+
file?: string;
|
|
192
193
|
rpcEndpoint?: string;
|
|
193
194
|
remote?: boolean;
|
|
194
195
|
hasAuth?: boolean;
|
|
@@ -258,6 +259,10 @@ export interface SerializableInspectorState {
|
|
|
258
259
|
definitions: InspectorState['systemRoles']['definitions'];
|
|
259
260
|
files: string[];
|
|
260
261
|
};
|
|
262
|
+
featureFlags: {
|
|
263
|
+
definitions: InspectorState['featureFlags']['definitions'];
|
|
264
|
+
files: string[];
|
|
265
|
+
};
|
|
261
266
|
personas: {
|
|
262
267
|
definitions: InspectorState['personas']['definitions'];
|
|
263
268
|
files: string[];
|
|
@@ -270,6 +275,7 @@ export interface SerializableInspectorState {
|
|
|
270
275
|
middleware: {
|
|
271
276
|
definitions: InspectorState['middleware']['definitions'];
|
|
272
277
|
instances: InspectorState['middleware']['instances'];
|
|
278
|
+
globalFiles: string[];
|
|
273
279
|
tagMiddleware: Array<[
|
|
274
280
|
string,
|
|
275
281
|
InspectorState['middleware']['tagMiddleware'] extends Map<string, infer V> ? V : never
|
|
@@ -134,6 +134,10 @@ export function serializeInspectorState(state) {
|
|
|
134
134
|
definitions: state.systemRoles.definitions,
|
|
135
135
|
files: Array.from(state.systemRoles.files),
|
|
136
136
|
},
|
|
137
|
+
featureFlags: {
|
|
138
|
+
definitions: state.featureFlags.definitions,
|
|
139
|
+
files: Array.from(state.featureFlags.files),
|
|
140
|
+
},
|
|
137
141
|
personas: {
|
|
138
142
|
definitions: state.personas.definitions,
|
|
139
143
|
files: Array.from(state.personas.files),
|
|
@@ -146,6 +150,7 @@ export function serializeInspectorState(state) {
|
|
|
146
150
|
middleware: {
|
|
147
151
|
definitions: state.middleware.definitions,
|
|
148
152
|
instances: state.middleware.instances,
|
|
153
|
+
globalFiles: Array.from(state.middleware.globalFiles),
|
|
149
154
|
tagMiddleware: Array.from(state.middleware.tagMiddleware.entries()),
|
|
150
155
|
},
|
|
151
156
|
channelMiddleware: {
|
|
@@ -315,6 +320,10 @@ export function deserializeInspectorState(data) {
|
|
|
315
320
|
definitions: data.systemRoles?.definitions || [],
|
|
316
321
|
files: new Set(data.systemRoles?.files || []),
|
|
317
322
|
},
|
|
323
|
+
featureFlags: {
|
|
324
|
+
definitions: data.featureFlags?.definitions || [],
|
|
325
|
+
files: new Set(data.featureFlags?.files || []),
|
|
326
|
+
},
|
|
318
327
|
personas: {
|
|
319
328
|
definitions: data.personas?.definitions || [],
|
|
320
329
|
files: new Set(data.personas?.files || []),
|
|
@@ -327,6 +336,7 @@ export function deserializeInspectorState(data) {
|
|
|
327
336
|
middleware: {
|
|
328
337
|
definitions: data.middleware.definitions,
|
|
329
338
|
instances: data.middleware.instances || {},
|
|
339
|
+
globalFiles: new Set(data.middleware.globalFiles || []),
|
|
330
340
|
tagMiddleware: new Map(data.middleware.tagMiddleware),
|
|
331
341
|
},
|
|
332
342
|
channelMiddleware: {
|
package/dist/visit.js
CHANGED
|
@@ -24,11 +24,13 @@ import { addSecret } from './add/add-secret.js';
|
|
|
24
24
|
import { addCredential } from './add/add-credential.js';
|
|
25
25
|
import { addScope } from './add/add-scope.js';
|
|
26
26
|
import { addSystemRole } from './add/add-system-role.js';
|
|
27
|
+
import { addFeatureFlag } from './add/add-feature-flag.js';
|
|
27
28
|
import { addPersonas } from './add/add-personas.js';
|
|
28
29
|
import { addVariable } from './add/add-variable.js';
|
|
29
30
|
import { addWorkflowGraph } from './add/add-workflow-graph.js';
|
|
30
31
|
import { addFeature } from './add/add-feature.js';
|
|
31
32
|
import { addAgent } from './add/add-agent.js';
|
|
33
|
+
import { addAnalytics } from './add/add-analytics.js';
|
|
32
34
|
import { addAgentScorer } from './add/add-agent-scorer.js';
|
|
33
35
|
import { addApprovalDescription } from './add/add-approval-description.js';
|
|
34
36
|
export const visitSetup = (logger, checker, node, state, options) => {
|
|
@@ -40,6 +42,7 @@ export const visitSetup = (logger, checker, node, state, options) => {
|
|
|
40
42
|
addFileWithFactory(node, checker, state.wireServicesFactories, 'CreateWireServices', state);
|
|
41
43
|
addFileWithFactory(node, checker, state.configFactories, 'CreateConfig');
|
|
42
44
|
addFileWithFactory(node, checker, state.serverLifecycleFactories, 'ServerLifecycle');
|
|
45
|
+
addAnalytics(logger, node, checker, state);
|
|
43
46
|
addRPCInvocations(node, state, logger);
|
|
44
47
|
addWireAddon(node, state, logger);
|
|
45
48
|
addWireRemoteAddon(node, state, logger);
|
|
@@ -73,6 +76,7 @@ export const visitRoutes = (logger, checker, node, state, options) => {
|
|
|
73
76
|
addCredential(logger, node, checker, state, nextOptions);
|
|
74
77
|
addScope(logger, node, checker, state, nextOptions);
|
|
75
78
|
addSystemRole(logger, node, checker, state, nextOptions);
|
|
79
|
+
addFeatureFlag(logger, node, checker, state, nextOptions);
|
|
76
80
|
addPersonas(logger, node, checker, state, nextOptions);
|
|
77
81
|
addVariable(logger, node, checker, state, nextOptions);
|
|
78
82
|
addHTTPRoute(logger, node, checker, state, nextOptions);
|
package/package.json
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/inspector",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.82",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/pikkujs/pikku.git",
|
|
7
|
+
"directory": "packages/inspector"
|
|
8
|
+
},
|
|
4
9
|
"description": "TypeScript inspector for Pikku — discovers functions and wirings to drive code generation",
|
|
5
10
|
"author": "yasser.fadl@gmail.com",
|
|
6
11
|
"license": "BUSL-1.1",
|
|
@@ -18,6 +23,11 @@
|
|
|
18
23
|
"import": "./dist/schema-generator.js",
|
|
19
24
|
"default": "./dist/schema-generator.js"
|
|
20
25
|
},
|
|
26
|
+
"./surface": {
|
|
27
|
+
"types": "./dist/surface/collect-surface.d.ts",
|
|
28
|
+
"import": "./dist/surface/collect-surface.js",
|
|
29
|
+
"default": "./dist/surface/collect-surface.js"
|
|
30
|
+
},
|
|
21
31
|
"./workflow-graph": {
|
|
22
32
|
"types": "./dist/utils/workflow/graph/index.d.ts",
|
|
23
33
|
"import": "./dist/utils/workflow/graph/index.js",
|
|
@@ -28,11 +38,11 @@
|
|
|
28
38
|
"tsc": "tsc",
|
|
29
39
|
"build": "tsc -b",
|
|
30
40
|
"ncu": "npx npm-check-updates -x '/.*glob.*/'",
|
|
31
|
-
"release": "
|
|
41
|
+
"release": "bun run build && npm test",
|
|
32
42
|
"test": "bash run-tests.sh",
|
|
33
43
|
"test:watch": "bash run-tests.sh --watch",
|
|
34
44
|
"test:coverage": "bash run-tests.sh --coverage",
|
|
35
|
-
"prepublishOnly": "
|
|
45
|
+
"prepublishOnly": "bun run build"
|
|
36
46
|
},
|
|
37
47
|
"dependencies": {
|
|
38
48
|
"@openapi-contrib/json-schema-to-openapi-schema": "^4.3.4",
|
|
@@ -45,13 +55,13 @@
|
|
|
45
55
|
"zod-to-ts": "^2.1.0"
|
|
46
56
|
},
|
|
47
57
|
"peerDependencies": {
|
|
48
|
-
"@pikku/core": "^0.12.
|
|
58
|
+
"@pikku/core": "^0.12.113"
|
|
49
59
|
},
|
|
50
60
|
"devDependencies": {
|
|
51
|
-
"@pikku/core": "^0.12.
|
|
61
|
+
"@pikku/core": "^0.12.113",
|
|
52
62
|
"@types/node": "^24.13.3"
|
|
53
63
|
},
|
|
54
64
|
"engines": {
|
|
55
65
|
"node": ">=24"
|
|
56
66
|
}
|
|
57
|
-
}
|
|
67
|
+
}
|
package/run-tests.sh
CHANGED
|
@@ -34,20 +34,35 @@ files=($(find src -type f -name "*.test.ts"))
|
|
|
34
34
|
# Check if any files matched the pattern
|
|
35
35
|
if [ ${#files[@]} -eq 0 ]; then
|
|
36
36
|
echo "No test files found matching pattern: $pattern"
|
|
37
|
+
if [ "${STRICT_TEST_DISCOVERY}" = "1" ]; then
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
37
40
|
exit 0
|
|
38
41
|
fi
|
|
39
42
|
|
|
40
|
-
# Construct the
|
|
41
|
-
|
|
43
|
+
# Construct the bun command
|
|
44
|
+
#
|
|
45
|
+
# --parallel gives every test file its own worker process, which is what node's
|
|
46
|
+
# `--test` did. Several suites keep module-level caches (a TypeScript program, a
|
|
47
|
+
# temp fixture directory) that a shared process would leak between files.
|
|
48
|
+
#
|
|
49
|
+
# --timeout because bun caps a test at 5s by default where node had no cap, and
|
|
50
|
+
# the suites that build real TypeScript programs run well past that.
|
|
51
|
+
bun_cmd=(bun test --parallel --timeout 120000)
|
|
42
52
|
|
|
43
53
|
# Append options based on flags
|
|
44
54
|
if [ "$watch_mode" = true ]; then
|
|
45
|
-
|
|
55
|
+
# --watch reruns in one process; parallel workers have nothing to watch.
|
|
56
|
+
bun_cmd=(bun test --watch --timeout 120000)
|
|
46
57
|
fi
|
|
47
58
|
|
|
48
59
|
if [ "$coverage_mode" = true ]; then
|
|
49
|
-
|
|
60
|
+
# --coverage-dir=. keeps lcov.info at the package root: CI merges every
|
|
61
|
+
# package's report by prefixing its SF paths with the file's own directory,
|
|
62
|
+
# so a nested coverage/ directory would re-root them one level too deep.
|
|
63
|
+
bun_cmd+=(--coverage --coverage-reporter=lcov --coverage-reporter=text --coverage-dir=.)
|
|
64
|
+
export PIKKU_TEST_COVERAGE=1
|
|
50
65
|
fi
|
|
51
66
|
|
|
52
|
-
# Execute the
|
|
53
|
-
"${
|
|
67
|
+
# Execute the bun command with the expanded list of files
|
|
68
|
+
"${bun_cmd[@]}" "${files[@]}"
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import * as ts from 'typescript'
|
|
2
|
-
import type { AddWiring } from '../types.js'
|
|
3
|
-
import { ErrorCode } from '../error-codes.js'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Wiring helpers an addon must not call. Addons declare contracts with the
|
|
7
|
-
* define* helpers and export functions; the consuming app does the wiring via
|
|
8
|
-
* refHTTP / refChannel / refCLI. Service declarations remain allowed.
|
|
9
|
-
*/
|
|
10
|
-
const BANNED_WIRINGS = new Set([
|
|
11
|
-
'wireAddon',
|
|
12
|
-
'wireRemoteAddon',
|
|
13
|
-
'wireChannel',
|
|
14
|
-
'wireCLI',
|
|
15
|
-
'wireGateway',
|
|
16
|
-
'wireHTTP',
|
|
17
|
-
'wireHTTPRoutes',
|
|
18
|
-
'wireMCPPrompt',
|
|
19
|
-
'wireMCPResource',
|
|
20
|
-
'wireQueueWorker',
|
|
21
|
-
'wireScheduler',
|
|
22
|
-
'wireTrigger',
|
|
23
|
-
'wireTriggerSource',
|
|
24
|
-
])
|
|
25
|
-
|
|
26
|
-
const CONTRACT_DEFINERS = new Set([
|
|
27
|
-
'defineHTTPRoutes',
|
|
28
|
-
'defineChannelRoutes',
|
|
29
|
-
'defineCLICommands',
|
|
30
|
-
])
|
|
31
|
-
|
|
32
|
-
const hasHandlerProperty = (node: ts.Node): boolean => {
|
|
33
|
-
let found = false
|
|
34
|
-
const visit = (current: ts.Node) => {
|
|
35
|
-
if (found) return
|
|
36
|
-
if (
|
|
37
|
-
ts.isPropertyAssignment(current) &&
|
|
38
|
-
(ts.isIdentifier(current.name) || ts.isStringLiteral(current.name)) &&
|
|
39
|
-
(current.name.text === 'middleware' ||
|
|
40
|
-
current.name.text === 'permissions')
|
|
41
|
-
) {
|
|
42
|
-
found = true
|
|
43
|
-
return
|
|
44
|
-
}
|
|
45
|
-
ts.forEachChild(current, visit)
|
|
46
|
-
}
|
|
47
|
-
visit(node)
|
|
48
|
-
return found
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Enforce addon authoring rules. Only runs when inspecting an addon package
|
|
53
|
-
* (options.isAddon). Addons cannot wire transports, and their contracts cannot
|
|
54
|
-
* carry middleware or permissions — those are the consuming app's concern.
|
|
55
|
-
*/
|
|
56
|
-
export const checkAddonBans: AddWiring = (
|
|
57
|
-
logger,
|
|
58
|
-
node,
|
|
59
|
-
_checker,
|
|
60
|
-
_state,
|
|
61
|
-
options
|
|
62
|
-
) => {
|
|
63
|
-
if (!options.isAddon) return
|
|
64
|
-
if (!ts.isCallExpression(node) || !ts.isIdentifier(node.expression)) return
|
|
65
|
-
|
|
66
|
-
const name = node.expression.text
|
|
67
|
-
|
|
68
|
-
if (BANNED_WIRINGS.has(name)) {
|
|
69
|
-
logger.critical(
|
|
70
|
-
ErrorCode.ADDON_WIRING_NOT_ALLOWED,
|
|
71
|
-
`Addons must not call '${name}'. Declare contracts with define* and export functions; the consuming app wires them via refHTTP / refChannel / refCLI.`
|
|
72
|
-
)
|
|
73
|
-
return
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (CONTRACT_DEFINERS.has(name)) {
|
|
77
|
-
const [arg] = node.arguments
|
|
78
|
-
if (arg && hasHandlerProperty(arg)) {
|
|
79
|
-
logger.critical(
|
|
80
|
-
ErrorCode.ADDON_CONTRACT_HANDLERS_NOT_ALLOWED,
|
|
81
|
-
`Addon contract '${name}' must not declare middleware or permissions — these are applied by the consuming app, not the addon.`
|
|
82
|
-
)
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|