@pikku/inspector 0.12.0 → 0.12.1
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 +25 -0
- package/dist/add/add-ai-agent.d.ts +1 -1
- package/dist/add/add-ai-agent.js +1 -1
- package/dist/add/add-channel.js +12 -0
- package/dist/add/add-cli.d.ts +1 -1
- package/dist/add/add-cli.js +6 -1
- package/dist/add/add-file-extends-core-type.d.ts +1 -1
- package/dist/add/add-file-with-config.d.ts +1 -1
- package/dist/add/add-file-with-factory.d.ts +1 -1
- package/dist/add/add-file-with-factory.js +2 -2
- package/dist/add/add-functions.d.ts +1 -1
- package/dist/add/add-functions.js +6 -7
- package/dist/add/add-http-route.d.ts +3 -2
- package/dist/add/add-http-route.js +5 -1
- package/dist/add/add-http-routes.d.ts +1 -1
- package/dist/add/add-http-routes.js +1 -0
- package/dist/add/add-keyed-wiring.d.ts +1 -1
- package/dist/add/add-mcp-prompt.d.ts +1 -1
- package/dist/add/add-mcp-resource.d.ts +1 -1
- package/dist/add/add-queue-worker.d.ts +1 -1
- package/dist/add/add-rpc-invocations.d.ts +3 -3
- package/dist/add/add-rpc-invocations.js +10 -10
- package/dist/add/add-schedule.d.ts +1 -1
- package/dist/add/add-secret.d.ts +1 -1
- package/dist/add/add-trigger.d.ts +1 -1
- package/dist/add/add-trigger.js +2 -2
- package/dist/add/add-wire-addon.d.ts +7 -0
- package/dist/add/add-wire-addon.js +70 -0
- package/dist/add/add-workflow.d.ts +1 -1
- package/dist/error-codes.d.ts +1 -0
- package/dist/error-codes.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/inspector.d.ts +1 -1
- package/dist/inspector.js +7 -4
- package/dist/types.d.ts +26 -20
- package/dist/utils/compute-required-schemas.js +1 -2
- package/dist/utils/contract-hashes.d.ts +20 -3
- package/dist/utils/contract-hashes.js +77 -10
- package/dist/utils/custom-types-generator.d.ts +1 -1
- package/dist/utils/detect-schema-vendor.d.ts +1 -1
- package/dist/utils/does-type-extend-core-type.d.ts +1 -1
- package/dist/utils/ensure-function-metadata.d.ts +1 -1
- package/dist/utils/extract-services.d.ts +1 -1
- package/dist/utils/filter-inspector-state.d.ts +1 -1
- package/dist/utils/filter-utils.d.ts +2 -2
- package/dist/utils/get-files-and-methods.d.ts +1 -1
- package/dist/utils/middleware.d.ts +2 -2
- package/dist/utils/permissions.d.ts +2 -2
- package/dist/utils/post-process.d.ts +4 -3
- package/dist/utils/post-process.js +24 -8
- package/dist/utils/resolve-addon-package.d.ts +16 -0
- package/dist/utils/{resolve-external-package.js → resolve-addon-package.js} +8 -8
- package/dist/utils/schema-generator.d.ts +2 -2
- package/dist/utils/serialize-inspector-state.d.ts +13 -3
- package/dist/utils/serialize-inspector-state.js +6 -2
- package/dist/utils/validate-auth-sessionless.d.ts +3 -0
- package/dist/utils/validate-auth-sessionless.js +14 -0
- package/dist/utils/workflow/dsl/extract-dsl-workflow.d.ts +1 -1
- package/dist/visit.d.ts +1 -1
- package/dist/visit.js +2 -0
- package/package.json +2 -2
- package/src/add/add-ai-agent.ts +2 -2
- package/src/add/add-channel.ts +21 -0
- package/src/add/add-cli.ts +19 -4
- package/src/add/add-file-extends-core-type.ts +1 -1
- package/src/add/add-file-with-config.ts +1 -1
- package/src/add/add-file-with-factory.ts +3 -3
- package/src/add/add-functions.ts +21 -19
- package/src/add/add-http-route.ts +19 -2
- package/src/add/add-http-routes.ts +2 -1
- package/src/add/add-keyed-wiring.ts +1 -1
- package/src/add/add-mcp-prompt.ts +1 -1
- package/src/add/add-mcp-resource.ts +1 -1
- package/src/add/add-queue-worker.ts +1 -1
- package/src/add/add-rpc-invocations.ts +11 -11
- package/src/add/add-schedule.ts +1 -1
- package/src/add/add-secret.ts +1 -1
- package/src/add/add-trigger.ts +4 -4
- package/src/add/add-wire-addon.ts +80 -0
- package/src/add/add-workflow.ts +2 -2
- package/src/error-codes.ts +1 -0
- package/src/index.ts +1 -0
- package/src/inspector.ts +13 -5
- package/src/types.ts +33 -20
- package/src/utils/compute-required-schemas.ts +1 -2
- package/src/utils/contract-hashes.test.ts +30 -5
- package/src/utils/contract-hashes.ts +110 -14
- package/src/utils/custom-types-generator.ts +1 -1
- package/src/utils/detect-schema-vendor.ts +1 -1
- package/src/utils/does-type-extend-core-type.ts +1 -1
- package/src/utils/ensure-function-metadata.ts +1 -1
- package/src/utils/extract-services.ts +1 -1
- package/src/utils/filter-inspector-state.test.ts +3 -5
- package/src/utils/filter-inspector-state.ts +7 -2
- package/src/utils/filter-utils.test.ts +1 -1
- package/src/utils/filter-utils.ts +2 -2
- package/src/utils/get-files-and-methods.ts +1 -1
- package/src/utils/middleware.ts +2 -2
- package/src/utils/permissions.ts +2 -2
- package/src/utils/post-process.ts +34 -12
- package/src/utils/{resolve-external-package.ts → resolve-addon-package.ts} +17 -10
- package/src/utils/resolve-versions.test.ts +1 -1
- package/src/utils/schema-generator.ts +4 -4
- package/src/utils/serialize-inspector-state.ts +23 -5
- package/src/utils/validate-auth-sessionless.ts +29 -0
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +2 -2
- package/src/visit.ts +7 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/resolve-external-package.d.ts +0 -12
|
@@ -1,18 +1,35 @@
|
|
|
1
|
-
import { FunctionsMeta, JSONValue } from '@pikku/core';
|
|
2
|
-
import { TypesMap } from '../types-map.js';
|
|
1
|
+
import type { FunctionsMeta, JSONValue } from '@pikku/core';
|
|
2
|
+
import type { TypesMap } from '../types-map.js';
|
|
3
3
|
import { ErrorCode } from '../error-codes.js';
|
|
4
4
|
export type ContractEntry = {
|
|
5
5
|
functionKey: string;
|
|
6
6
|
version: number;
|
|
7
7
|
contractHash: string;
|
|
8
|
+
inputHash: string;
|
|
9
|
+
outputHash: string;
|
|
8
10
|
};
|
|
9
11
|
export type VersionValidateError = {
|
|
10
12
|
code: ErrorCode;
|
|
11
13
|
message: string;
|
|
14
|
+
functionKey?: string;
|
|
15
|
+
version?: number;
|
|
16
|
+
previousHash?: string;
|
|
17
|
+
currentHash?: string;
|
|
18
|
+
previousInputHash?: string;
|
|
19
|
+
currentInputHash?: string;
|
|
20
|
+
previousOutputHash?: string;
|
|
21
|
+
currentOutputHash?: string;
|
|
22
|
+
nextVersion?: number;
|
|
23
|
+
latestVersion?: number;
|
|
24
|
+
expectedNextVersion?: number;
|
|
25
|
+
};
|
|
26
|
+
export type VersionHashEntry = {
|
|
27
|
+
inputHash: string;
|
|
28
|
+
outputHash: string;
|
|
12
29
|
};
|
|
13
30
|
export type VersionManifestEntry = {
|
|
14
31
|
latest: number;
|
|
15
|
-
versions: Record<string, string>;
|
|
32
|
+
versions: Record<string, string | VersionHashEntry>;
|
|
16
33
|
};
|
|
17
34
|
export type VersionManifest = {
|
|
18
35
|
manifestVersion: 1;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { parseVersionedId } from '@pikku/core';
|
|
2
2
|
import { ErrorCode } from '../error-codes.js';
|
|
3
3
|
import { canonicalJSON, hashString } from './hash.js';
|
|
4
|
+
function isHashEntry(v) {
|
|
5
|
+
return typeof v === 'object';
|
|
6
|
+
}
|
|
4
7
|
export function createEmptyManifest() {
|
|
5
8
|
return {
|
|
6
9
|
manifestVersion: 1,
|
|
@@ -32,6 +35,12 @@ export function serializeManifest(manifest) {
|
|
|
32
35
|
export function computeContractHash(data) {
|
|
33
36
|
return hashString(canonicalJSON(data), 16);
|
|
34
37
|
}
|
|
38
|
+
function computeInputHash(functionKey, inputSchema) {
|
|
39
|
+
return hashString(canonicalJSON({ functionKey, inputSchema }), 8);
|
|
40
|
+
}
|
|
41
|
+
function computeOutputHash(functionKey, outputSchema) {
|
|
42
|
+
return hashString(canonicalJSON({ functionKey, outputSchema }), 8);
|
|
43
|
+
}
|
|
35
44
|
function resolveSchema(typeNames, allSchemas, typesMap) {
|
|
36
45
|
if (!typeNames) {
|
|
37
46
|
return null;
|
|
@@ -75,7 +84,15 @@ export function buildCurrentContracts(functionsMeta, allSchemas, typesMap) {
|
|
|
75
84
|
inputSchema,
|
|
76
85
|
outputSchema,
|
|
77
86
|
});
|
|
78
|
-
|
|
87
|
+
const inputHash = computeInputHash(functionKey, inputSchema);
|
|
88
|
+
const outputHash = computeOutputHash(functionKey, outputSchema);
|
|
89
|
+
result.set(funcId, {
|
|
90
|
+
functionKey,
|
|
91
|
+
version,
|
|
92
|
+
contractHash,
|
|
93
|
+
inputHash,
|
|
94
|
+
outputHash,
|
|
95
|
+
});
|
|
79
96
|
}
|
|
80
97
|
return result;
|
|
81
98
|
}
|
|
@@ -85,6 +102,8 @@ export function computeContractHashes(allSchemas, typesMap, functionsMeta) {
|
|
|
85
102
|
const meta = functionsMeta[funcId];
|
|
86
103
|
if (meta) {
|
|
87
104
|
meta.contractHash = entry.contractHash;
|
|
105
|
+
meta.inputHash = entry.inputHash;
|
|
106
|
+
meta.outputHash = entry.outputHash;
|
|
88
107
|
}
|
|
89
108
|
}
|
|
90
109
|
return contracts;
|
|
@@ -98,6 +117,13 @@ function groupByFunctionKey(contracts) {
|
|
|
98
117
|
}
|
|
99
118
|
return grouped;
|
|
100
119
|
}
|
|
120
|
+
function entryChanged(existing, current) {
|
|
121
|
+
if (isHashEntry(existing)) {
|
|
122
|
+
return (existing.inputHash !== current.inputHash ||
|
|
123
|
+
existing.outputHash !== current.outputHash);
|
|
124
|
+
}
|
|
125
|
+
return existing !== current.contractHash;
|
|
126
|
+
}
|
|
101
127
|
export function validateContracts(manifest, currentContracts) {
|
|
102
128
|
const errors = [];
|
|
103
129
|
const grouped = groupByFunctionKey(currentContracts);
|
|
@@ -107,14 +133,31 @@ export function validateContracts(manifest, currentContracts) {
|
|
|
107
133
|
if (!manifestEntry) {
|
|
108
134
|
continue;
|
|
109
135
|
}
|
|
110
|
-
for (const
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
136
|
+
for (const current of entries) {
|
|
137
|
+
const { version, contractHash, inputHash, outputHash } = current;
|
|
138
|
+
const existingEntry = manifestEntry.versions[String(version)];
|
|
139
|
+
if (existingEntry !== undefined) {
|
|
140
|
+
if (entryChanged(existingEntry, current)) {
|
|
114
141
|
reportedKeys.add(`${functionKey}@${version}`);
|
|
142
|
+
const prevInputHash = isHashEntry(existingEntry)
|
|
143
|
+
? existingEntry.inputHash
|
|
144
|
+
: existingEntry;
|
|
145
|
+
const prevOutputHash = isHashEntry(existingEntry)
|
|
146
|
+
? existingEntry.outputHash
|
|
147
|
+
: existingEntry;
|
|
115
148
|
errors.push({
|
|
116
149
|
code: ErrorCode.FUNCTION_VERSION_MODIFIED,
|
|
117
|
-
message: `Contract for ${functionKey}@v${version} has changed (recorded: ${
|
|
150
|
+
message: `Contract for ${functionKey}@v${version} has changed (recorded: ${isHashEntry(existingEntry) ? `${existingEntry.inputHash}/${existingEntry.outputHash}` : existingEntry}, current: ${contractHash}). Existing versions are immutable.`,
|
|
151
|
+
functionKey,
|
|
152
|
+
version,
|
|
153
|
+
previousHash: isHashEntry(existingEntry)
|
|
154
|
+
? existingEntry.inputHash
|
|
155
|
+
: existingEntry,
|
|
156
|
+
currentHash: contractHash,
|
|
157
|
+
previousInputHash: prevInputHash,
|
|
158
|
+
currentInputHash: inputHash,
|
|
159
|
+
previousOutputHash: prevOutputHash,
|
|
160
|
+
currentOutputHash: outputHash,
|
|
118
161
|
});
|
|
119
162
|
}
|
|
120
163
|
}
|
|
@@ -123,30 +166,49 @@ export function validateContracts(manifest, currentContracts) {
|
|
|
123
166
|
errors.push({
|
|
124
167
|
code: ErrorCode.VERSION_REGRESSION_OR_CONFLICT,
|
|
125
168
|
message: `Version ${version} for ${functionKey} is <= latest (${manifestEntry.latest}) but not recorded. Possible merge conflict.`,
|
|
169
|
+
functionKey,
|
|
170
|
+
version,
|
|
171
|
+
latestVersion: manifestEntry.latest,
|
|
126
172
|
});
|
|
127
173
|
}
|
|
128
174
|
else if (version > manifestEntry.latest + 1) {
|
|
129
175
|
errors.push({
|
|
130
176
|
code: ErrorCode.VERSION_GAP_NOT_ALLOWED,
|
|
131
177
|
message: `Version ${version} for ${functionKey} skips versions. Latest is ${manifestEntry.latest}, next must be ${manifestEntry.latest + 1}.`,
|
|
178
|
+
functionKey,
|
|
179
|
+
version,
|
|
180
|
+
latestVersion: manifestEntry.latest,
|
|
181
|
+
expectedNextVersion: manifestEntry.latest + 1,
|
|
132
182
|
});
|
|
133
183
|
}
|
|
134
184
|
}
|
|
135
185
|
}
|
|
136
186
|
}
|
|
137
187
|
for (const [functionKey, manifestEntry] of Object.entries(manifest.contracts)) {
|
|
138
|
-
const
|
|
188
|
+
const latestEntry = manifestEntry.versions[String(manifestEntry.latest)];
|
|
139
189
|
const currentEntries = grouped.get(functionKey);
|
|
140
190
|
if (!currentEntries) {
|
|
141
191
|
continue;
|
|
142
192
|
}
|
|
143
193
|
const currentLatest = currentEntries.find((e) => e.version === manifestEntry.latest);
|
|
144
194
|
if (currentLatest &&
|
|
145
|
-
currentLatest
|
|
195
|
+
entryChanged(latestEntry, currentLatest) &&
|
|
146
196
|
!reportedKeys.has(`${functionKey}@${manifestEntry.latest}`)) {
|
|
197
|
+
const prevInputHash = isHashEntry(latestEntry)
|
|
198
|
+
? latestEntry.inputHash
|
|
199
|
+
: undefined;
|
|
200
|
+
const prevOutputHash = isHashEntry(latestEntry)
|
|
201
|
+
? latestEntry.outputHash
|
|
202
|
+
: undefined;
|
|
147
203
|
errors.push({
|
|
148
204
|
code: ErrorCode.CONTRACT_CHANGED_REQUIRES_BUMP,
|
|
149
205
|
message: `Contract for ${functionKey} changed. Set \`version: ${manifestEntry.latest + 1}\` on the function or run 'pikku versions update'.`,
|
|
206
|
+
functionKey,
|
|
207
|
+
previousInputHash: prevInputHash,
|
|
208
|
+
currentInputHash: currentLatest.inputHash,
|
|
209
|
+
previousOutputHash: prevOutputHash,
|
|
210
|
+
currentOutputHash: currentLatest.outputHash,
|
|
211
|
+
nextVersion: manifestEntry.latest + 1,
|
|
150
212
|
});
|
|
151
213
|
}
|
|
152
214
|
}
|
|
@@ -160,6 +222,9 @@ export function validateContracts(manifest, currentContracts) {
|
|
|
160
222
|
errors.push({
|
|
161
223
|
code: ErrorCode.MANIFEST_INTEGRITY_ERROR,
|
|
162
224
|
message: `Manifest integrity error for ${functionKey}: latest field (${manifestEntry.latest}) inconsistent with version keys (max: ${maxVersion}).`,
|
|
225
|
+
functionKey,
|
|
226
|
+
latestVersion: manifestEntry.latest,
|
|
227
|
+
expectedNextVersion: maxVersion,
|
|
163
228
|
});
|
|
164
229
|
}
|
|
165
230
|
}
|
|
@@ -176,8 +241,8 @@ export function updateManifest(existing, currentContracts) {
|
|
|
176
241
|
manifest.contracts[functionKey] = { latest: 0, versions: {} };
|
|
177
242
|
}
|
|
178
243
|
const entry = manifest.contracts[functionKey];
|
|
179
|
-
for (const { version,
|
|
180
|
-
entry.versions[String(version)] =
|
|
244
|
+
for (const { version, inputHash, outputHash } of entries) {
|
|
245
|
+
entry.versions[String(version)] = { inputHash, outputHash };
|
|
181
246
|
entry.latest = Math.max(entry.latest, version);
|
|
182
247
|
}
|
|
183
248
|
}
|
|
@@ -196,6 +261,8 @@ export function extractContractsFromMeta(functionsMeta) {
|
|
|
196
261
|
functionKey,
|
|
197
262
|
version,
|
|
198
263
|
contractHash: meta.contractHash,
|
|
264
|
+
inputHash: meta.inputHash ?? '',
|
|
265
|
+
outputHash: meta.outputHash ?? '',
|
|
199
266
|
});
|
|
200
267
|
}
|
|
201
268
|
return result;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import * as ts from 'typescript';
|
|
1
|
+
import type * as ts from 'typescript';
|
|
2
2
|
export declare const doesTypeExtendsCore: (type: ts.Type, checker: ts.TypeChecker, visitedTypes: Set<ts.Type>, coreType: string) => boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
-
import { InspectorState } from '../types.js';
|
|
2
|
+
import type { InspectorState } from '../types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Ensures that function metadata exists for a given pikkuFuncId.
|
|
5
5
|
* Creates stub metadata if it doesn't exist (useful for inline functions).
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
-
import { FunctionServicesMeta, FunctionWiresMeta } from '@pikku/core';
|
|
2
|
+
import type { FunctionServicesMeta, FunctionWiresMeta } from '@pikku/core';
|
|
3
3
|
/**
|
|
4
4
|
* Extract services from a function's first parameter destructuring pattern
|
|
5
5
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { InspectorState, InspectorFilters, InspectorLogger } from '../types.js';
|
|
1
|
+
import type { InspectorState, InspectorFilters, InspectorLogger } from '../types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Filters inspector state based on provided filters
|
|
4
4
|
* This is applied post-inspection to support the inspect-once, filter-many pattern
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { InspectorFilters, InspectorLogger } from '../types.js';
|
|
2
|
-
import { PikkuWiringTypes } from '@pikku/core';
|
|
1
|
+
import type { InspectorFilters, InspectorLogger } from '../types.js';
|
|
2
|
+
import type { PikkuWiringTypes } from '@pikku/core';
|
|
3
3
|
/**
|
|
4
4
|
* Match a value against a pattern with wildcard support
|
|
5
5
|
* Supports "*" at the beginning, end, or both (e.g., "send*", "*Payment", "*process*")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
-
import { MiddlewareMetadata } from '@pikku/core';
|
|
3
|
-
import { InspectorState } from '../types.js';
|
|
2
|
+
import type { MiddlewareMetadata } from '@pikku/core';
|
|
3
|
+
import type { InspectorState } from '../types.js';
|
|
4
4
|
export interface MiddlewareRef {
|
|
5
5
|
definitionId: string;
|
|
6
6
|
isFactoryCall: boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
-
import { PermissionMetadata } from '@pikku/core';
|
|
3
|
-
import { InspectorState } from '../types.js';
|
|
2
|
+
import type { PermissionMetadata } from '@pikku/core';
|
|
3
|
+
import type { InspectorState } from '../types.js';
|
|
4
4
|
/**
|
|
5
5
|
* Extract permission pikkuFuncIds from an expression (array or object literal)
|
|
6
6
|
* Resolves each identifier to its pikkuFuncId using extractFunctionName
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { InspectorState, InspectorLogger, InspectorOptions, InspectorModelConfig
|
|
2
|
-
import { MiddlewareMetadata, PermissionMetadata } from '@pikku/core';
|
|
1
|
+
import type { InspectorState, InspectorLogger, InspectorOptions, InspectorModelConfig } from '../types.js';
|
|
2
|
+
import type { MiddlewareMetadata, PermissionMetadata } from '@pikku/core';
|
|
3
3
|
/**
|
|
4
4
|
* Helper to extract wire-level middleware/permission names from metadata.
|
|
5
5
|
* Only extracts type:'wire' variants (individual middleware/permissions).
|
|
@@ -14,7 +14,8 @@ export declare function extractWireNames(list?: MiddlewareMetadata[] | Permissio
|
|
|
14
14
|
* in the add-* methods during AST traversal for efficiency.
|
|
15
15
|
*/
|
|
16
16
|
export declare function aggregateRequiredServices(state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
|
|
17
|
-
export declare function validateSecretOverrides(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'
|
|
17
|
+
export declare function validateSecretOverrides(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
|
|
18
|
+
export declare function validateVariableOverrides(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
|
|
18
19
|
export declare function computeResolvedIOTypes(state: InspectorState): void;
|
|
19
20
|
export declare function computeMiddlewareGroupsMeta(state: InspectorState): void;
|
|
20
21
|
export declare function computePermissionsGroupsMeta(state: InspectorState): void;
|
|
@@ -161,17 +161,34 @@ export function aggregateRequiredServices(state) {
|
|
|
161
161
|
});
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
|
-
export function validateSecretOverrides(logger, state
|
|
165
|
-
|
|
164
|
+
export function validateSecretOverrides(logger, state) {
|
|
165
|
+
const { wireAddonDeclarations } = state.rpc;
|
|
166
|
+
if (!wireAddonDeclarations || wireAddonDeclarations.size === 0)
|
|
166
167
|
return;
|
|
167
168
|
const secretNames = new Set(state.secrets.definitions.map((d) => d.name));
|
|
168
|
-
for (const [namespace,
|
|
169
|
-
if (!
|
|
169
|
+
for (const [namespace, addonDecl] of wireAddonDeclarations.entries()) {
|
|
170
|
+
if (!addonDecl.secretOverrides)
|
|
170
171
|
continue;
|
|
171
|
-
for (const secretKey of Object.keys(
|
|
172
|
+
for (const secretKey of Object.keys(addonDecl.secretOverrides)) {
|
|
172
173
|
if (!secretNames.has(secretKey)) {
|
|
173
174
|
const availableSecrets = Array.from(secretNames);
|
|
174
|
-
logger.critical(ErrorCode.INVALID_VALUE, `Secret override '${secretKey}' in
|
|
175
|
+
logger.critical(ErrorCode.INVALID_VALUE, `Secret override '${secretKey}' in addon '${namespace}' (${addonDecl.package}) does not exist. Available secrets: ${availableSecrets.join(', ') || 'none'}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
export function validateVariableOverrides(logger, state) {
|
|
181
|
+
const { wireAddonDeclarations } = state.rpc;
|
|
182
|
+
if (!wireAddonDeclarations || wireAddonDeclarations.size === 0)
|
|
183
|
+
return;
|
|
184
|
+
const variableNames = new Set(state.variables.definitions.map((d) => d.name));
|
|
185
|
+
for (const [namespace, addonDecl] of wireAddonDeclarations.entries()) {
|
|
186
|
+
if (!addonDecl.variableOverrides)
|
|
187
|
+
continue;
|
|
188
|
+
for (const variableKey of Object.keys(addonDecl.variableOverrides)) {
|
|
189
|
+
if (!variableNames.has(variableKey)) {
|
|
190
|
+
const availableVariables = Array.from(variableNames);
|
|
191
|
+
logger.critical(ErrorCode.INVALID_VALUE, `Variable override '${variableKey}' in addon '${namespace}' (${addonDecl.package}) does not exist. Available variables: ${availableVariables.join(', ') || 'none'}`);
|
|
175
192
|
}
|
|
176
193
|
}
|
|
177
194
|
}
|
|
@@ -277,7 +294,7 @@ export function computeRequiredSchemas(state, options) {
|
|
|
277
294
|
const schemasFromTypes = options.schemaConfig?.schemasFromTypes;
|
|
278
295
|
state.requiredSchemas = new Set([
|
|
279
296
|
...Object.values(functions.meta)
|
|
280
|
-
.
|
|
297
|
+
.flatMap(({ inputs, outputs }) => {
|
|
281
298
|
const types = [];
|
|
282
299
|
if (inputs?.[0]) {
|
|
283
300
|
try {
|
|
@@ -297,7 +314,6 @@ export function computeRequiredSchemas(state, options) {
|
|
|
297
314
|
}
|
|
298
315
|
return types;
|
|
299
316
|
})
|
|
300
|
-
.flat()
|
|
301
317
|
.filter((s) => !!s && !PRIMITIVE_TYPES.has(s)),
|
|
302
318
|
...functions.typesMap.customTypes.keys(),
|
|
303
319
|
...(schemasFromTypes || []),
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the addon package name from an imported identifier.
|
|
4
|
+
* Checks if the identifier's import module specifier matches any
|
|
5
|
+
* configured addon package.
|
|
6
|
+
*
|
|
7
|
+
* This is a general utility — any wire handler that processes a `func`
|
|
8
|
+
* property can use it to detect when the function comes from an
|
|
9
|
+
* addon package.
|
|
10
|
+
*/
|
|
11
|
+
export declare const resolveAddonName: (identifier: ts.Identifier, checker: ts.TypeChecker, wireAddonDeclarations?: Map<string, {
|
|
12
|
+
package: string;
|
|
13
|
+
rpcEndpoint?: string;
|
|
14
|
+
secretOverrides?: Record<string, string>;
|
|
15
|
+
variableOverrides?: Record<string, string>;
|
|
16
|
+
}>) => string | null;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
2
|
/**
|
|
3
|
-
* Resolve the
|
|
3
|
+
* Resolve the addon package name from an imported identifier.
|
|
4
4
|
* Checks if the identifier's import module specifier matches any
|
|
5
|
-
* configured
|
|
5
|
+
* configured addon package.
|
|
6
6
|
*
|
|
7
7
|
* This is a general utility — any wire handler that processes a `func`
|
|
8
8
|
* property can use it to detect when the function comes from an
|
|
9
|
-
*
|
|
9
|
+
* addon package.
|
|
10
10
|
*/
|
|
11
|
-
export const
|
|
12
|
-
if (!
|
|
11
|
+
export const resolveAddonName = (identifier, checker, wireAddonDeclarations) => {
|
|
12
|
+
if (!wireAddonDeclarations || wireAddonDeclarations.size === 0) {
|
|
13
13
|
return null;
|
|
14
14
|
}
|
|
15
15
|
const sym = checker.getSymbolAtLocation(identifier);
|
|
@@ -25,9 +25,9 @@ export const resolveExternalPackageName = (identifier, checker, externalPackages
|
|
|
25
25
|
if (!ts.isStringLiteral(importDecl.moduleSpecifier))
|
|
26
26
|
return null;
|
|
27
27
|
const moduleSpecifier = importDecl.moduleSpecifier.text;
|
|
28
|
-
for (const
|
|
29
|
-
if (
|
|
30
|
-
return
|
|
28
|
+
for (const addonDecl of wireAddonDeclarations.values()) {
|
|
29
|
+
if (addonDecl.package === moduleSpecifier) {
|
|
30
|
+
return addonDecl.package;
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
return null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { JSONValue } from '@pikku/core';
|
|
2
|
-
import { InspectorLogger, InspectorState } from '../types.js';
|
|
1
|
+
import type { JSONValue } from '@pikku/core';
|
|
2
|
+
import type { InspectorLogger, InspectorState } from '../types.js';
|
|
3
3
|
export declare function generateAllSchemas(logger: InspectorLogger, config: {
|
|
4
4
|
tsconfig: string;
|
|
5
5
|
schemasFromTypes?: string[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { JSONValue } from '@pikku/core';
|
|
2
|
-
import { InspectorDiagnostic, InspectorState } from '../types.js';
|
|
1
|
+
import type { JSONValue } from '@pikku/core';
|
|
2
|
+
import type { InspectorDiagnostic, InspectorState } from '../types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Serializable version of InspectorState that can be saved to JSON
|
|
5
5
|
* Omits typesLookup (contains non-serializable ts.Type objects) and converts Maps/Sets to arrays
|
|
@@ -163,7 +163,17 @@ export interface SerializableInspectorState {
|
|
|
163
163
|
exportedName: string;
|
|
164
164
|
}]>;
|
|
165
165
|
invokedFunctions: string[];
|
|
166
|
-
|
|
166
|
+
usedAddons: string[];
|
|
167
|
+
wireAddonDeclarations: Array<[
|
|
168
|
+
string,
|
|
169
|
+
{
|
|
170
|
+
package: string;
|
|
171
|
+
rpcEndpoint?: string;
|
|
172
|
+
secretOverrides?: Record<string, string>;
|
|
173
|
+
variableOverrides?: Record<string, string>;
|
|
174
|
+
}
|
|
175
|
+
]>;
|
|
176
|
+
wireAddonFiles: string[];
|
|
167
177
|
};
|
|
168
178
|
mcpEndpoints: {
|
|
169
179
|
resourcesMeta: InspectorState['mcpEndpoints']['resourcesMeta'];
|
|
@@ -70,7 +70,9 @@ export function serializeInspectorState(state) {
|
|
|
70
70
|
exposedMeta: state.rpc.exposedMeta,
|
|
71
71
|
exposedFiles: Array.from(state.rpc.exposedFiles.entries()),
|
|
72
72
|
invokedFunctions: Array.from(state.rpc.invokedFunctions),
|
|
73
|
-
|
|
73
|
+
usedAddons: Array.from(state.rpc.usedAddons),
|
|
74
|
+
wireAddonDeclarations: Array.from(state.rpc.wireAddonDeclarations.entries()),
|
|
75
|
+
wireAddonFiles: Array.from(state.rpc.wireAddonFiles),
|
|
74
76
|
},
|
|
75
77
|
mcpEndpoints: {
|
|
76
78
|
resourcesMeta: state.mcpEndpoints.resourcesMeta,
|
|
@@ -205,7 +207,9 @@ export function deserializeInspectorState(data) {
|
|
|
205
207
|
exposedMeta: data.rpc.exposedMeta,
|
|
206
208
|
exposedFiles: new Map(data.rpc.exposedFiles),
|
|
207
209
|
invokedFunctions: new Set(data.rpc.invokedFunctions),
|
|
208
|
-
|
|
210
|
+
usedAddons: new Set(data.rpc.usedAddons || []),
|
|
211
|
+
wireAddonDeclarations: new Map(data.rpc.wireAddonDeclarations || []),
|
|
212
|
+
wireAddonFiles: new Set(data.rpc.wireAddonFiles || []),
|
|
209
213
|
},
|
|
210
214
|
mcpEndpoints: {
|
|
211
215
|
resourcesMeta: data.mcpEndpoints.resourcesMeta,
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
import type { InspectorLogger, InspectorState } from '../types.js';
|
|
3
|
+
export declare function validateAuthSessionless(logger: InspectorLogger, obj: ts.ObjectLiteralExpression, state: InspectorState, funcName: string, wireDescription: string, inheritedAuth?: boolean): boolean;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { getPropertyValue } from './get-property-value.js';
|
|
2
|
+
import { ErrorCode } from '../error-codes.js';
|
|
3
|
+
export function validateAuthSessionless(logger, obj, state, funcName, wireDescription, inheritedAuth) {
|
|
4
|
+
const fnMeta = state.functions.meta[funcName];
|
|
5
|
+
if (!fnMeta)
|
|
6
|
+
return true;
|
|
7
|
+
const routeAuth = getPropertyValue(obj, 'auth');
|
|
8
|
+
const resolvedAuth = routeAuth === true || routeAuth === false ? routeAuth : inheritedAuth;
|
|
9
|
+
if (resolvedAuth === false && fnMeta.sessionless === false) {
|
|
10
|
+
logger.critical(ErrorCode.AUTH_DISABLED_REQUIRES_SESSIONLESS, `${wireDescription} has auth disabled but function '${funcName}' uses pikkuFunc (requires session). Use pikkuSessionlessFunc instead.`);
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
return true;
|
|
14
|
+
}
|
package/dist/visit.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
-
import { InspectorState, InspectorLogger, InspectorOptions } from './types.js';
|
|
2
|
+
import type { InspectorState, InspectorLogger, InspectorOptions } from './types.js';
|
|
3
3
|
export declare const visitSetup: (logger: InspectorLogger, checker: ts.TypeChecker, node: ts.Node, state: InspectorState, options: InspectorOptions) => void;
|
|
4
4
|
export declare const visitRoutes: (logger: InspectorLogger, checker: ts.TypeChecker, node: ts.Node, state: InspectorState, options: InspectorOptions) => void;
|
package/dist/visit.js
CHANGED
|
@@ -12,6 +12,7 @@ import { addMCPPrompt } from './add/add-mcp-prompt.js';
|
|
|
12
12
|
import { addFunctions } from './add/add-functions.js';
|
|
13
13
|
import { addChannel } from './add/add-channel.js';
|
|
14
14
|
import { addRPCInvocations } from './add/add-rpc-invocations.js';
|
|
15
|
+
import { addWireAddon } from './add/add-wire-addon.js';
|
|
15
16
|
import { addMiddleware } from './add/add-middleware.js';
|
|
16
17
|
import { addPermission } from './add/add-permission.js';
|
|
17
18
|
import { addCLI, addCLIRenderers } from './add/add-cli.js';
|
|
@@ -28,6 +29,7 @@ export const visitSetup = (logger, checker, node, state, options) => {
|
|
|
28
29
|
addFileWithFactory(node, checker, state.wireServicesFactories, 'CreateWireServices', state);
|
|
29
30
|
addFileWithFactory(node, checker, state.configFactories, 'CreateConfig');
|
|
30
31
|
addRPCInvocations(node, state, logger);
|
|
32
|
+
addWireAddon(node, state, logger);
|
|
31
33
|
addMiddleware(logger, node, checker, state, options);
|
|
32
34
|
addPermission(logger, node, checker, state, options);
|
|
33
35
|
addWorkflow(logger, node, checker, state, options);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/inspector",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"author": "yasser.fadl@gmail.com",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"type": "module",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@openapi-contrib/json-schema-to-openapi-schema": "^4.3.1",
|
|
37
|
-
"@pikku/core": "^0.12.
|
|
37
|
+
"@pikku/core": "^0.12.1",
|
|
38
38
|
"path-to-regexp": "^8.3.0",
|
|
39
39
|
"ts-json-schema-generator": "^2.5.0",
|
|
40
40
|
"tsx": "^4.21.0",
|
package/src/add/add-ai-agent.ts
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
getCommonWireMetaData,
|
|
5
5
|
} from '../utils/get-property-value.js'
|
|
6
6
|
import { extractWireNames } from '../utils/post-process.js'
|
|
7
|
-
import { AddWiring, InspectorLogger, SchemaRef } from '../types.js'
|
|
7
|
+
import type { AddWiring, InspectorLogger, SchemaRef } from '../types.js'
|
|
8
8
|
import {
|
|
9
9
|
extractFunctionName,
|
|
10
10
|
funcIdToTypeName,
|
|
@@ -63,7 +63,7 @@ function resolveToolReferences(
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
if (calleeName === '
|
|
66
|
+
if (calleeName === 'addon') {
|
|
67
67
|
const [firstArg] = element.arguments
|
|
68
68
|
if (firstArg && ts.isStringLiteral(firstArg)) {
|
|
69
69
|
resolved.push(firstArg.text)
|
package/src/add/add-channel.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
} from '../utils/middleware.js'
|
|
19
19
|
import { extractWireNames } from '../utils/post-process.js'
|
|
20
20
|
import { resolveIdentifier } from '../utils/resolve-identifier.js'
|
|
21
|
+
import { validateAuthSessionless } from '../utils/validate-auth-sessionless.js'
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Safely get the "initializer" expression of a property-like AST node:
|
|
@@ -611,6 +612,26 @@ export const addChannel: AddWiring = (
|
|
|
611
612
|
state.serviceAggregation.usedMiddleware.add(name)
|
|
612
613
|
)
|
|
613
614
|
|
|
615
|
+
// --- validate auth/sessionless ---
|
|
616
|
+
const handlersToValidate = [
|
|
617
|
+
connectFuncId,
|
|
618
|
+
disconnectFuncId,
|
|
619
|
+
message?.pikkuFuncId,
|
|
620
|
+
].filter(Boolean) as string[]
|
|
621
|
+
for (const funcId of handlersToValidate) {
|
|
622
|
+
if (
|
|
623
|
+
!validateAuthSessionless(
|
|
624
|
+
logger,
|
|
625
|
+
obj,
|
|
626
|
+
state,
|
|
627
|
+
funcId,
|
|
628
|
+
`Channel '${name}'`
|
|
629
|
+
)
|
|
630
|
+
) {
|
|
631
|
+
return
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
614
635
|
state.channels.files.add(node.getSourceFile().fileName)
|
|
615
636
|
state.channels.meta[name] = {
|
|
616
637
|
name,
|