@pikku/inspector 0.11.2 → 0.12.0
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 +11 -1
- package/OPTIMIZATION-PLAN.md +195 -0
- package/dist/add/add-ai-agent.d.ts +2 -0
- package/dist/add/add-ai-agent.js +314 -0
- package/dist/add/add-channel.js +69 -61
- package/dist/add/add-cli.js +36 -18
- package/dist/add/add-file-with-factory.js +2 -0
- package/dist/add/add-functions.js +250 -75
- package/dist/add/add-http-route.d.ts +19 -10
- package/dist/add/add-http-route.js +152 -66
- package/dist/add/add-http-routes.d.ts +5 -0
- package/dist/add/add-http-routes.js +159 -0
- package/dist/add/add-keyed-wiring.d.ts +12 -0
- package/dist/add/add-keyed-wiring.js +97 -0
- package/dist/add/add-mcp-prompt.js +14 -9
- package/dist/add/add-mcp-resource.js +14 -9
- package/dist/add/add-middleware.d.ts +1 -4
- package/dist/add/add-middleware.js +364 -79
- package/dist/add/add-permission.d.ts +1 -1
- package/dist/add/add-permission.js +152 -40
- package/dist/add/add-queue-worker.js +18 -12
- package/dist/add/add-rpc-invocations.js +14 -0
- package/dist/add/add-schedule.js +11 -5
- package/dist/add/add-secret.d.ts +3 -0
- package/dist/add/add-secret.js +82 -0
- package/dist/add/add-trigger.d.ts +2 -0
- package/dist/add/add-trigger.js +87 -0
- package/dist/add/add-variable.d.ts +1 -0
- package/dist/add/add-variable.js +8 -0
- package/dist/add/add-workflow-graph.d.ts +3 -2
- package/dist/add/add-workflow-graph.js +143 -406
- package/dist/add/add-workflow.js +6 -4
- package/dist/error-codes.d.ts +14 -1
- package/dist/error-codes.js +19 -1
- package/dist/index.d.ts +9 -8
- package/dist/index.js +5 -4
- package/dist/inspector.d.ts +1 -1
- package/dist/inspector.js +91 -14
- package/dist/schema-generator.d.ts +1 -0
- package/dist/schema-generator.js +1 -0
- package/dist/types-map.js +10 -1
- package/dist/types.d.ts +163 -39
- package/dist/utils/compute-required-schemas.d.ts +4 -0
- package/dist/utils/compute-required-schemas.js +41 -0
- package/dist/utils/contract-hashes.d.ts +35 -0
- package/dist/utils/contract-hashes.js +202 -0
- package/dist/utils/custom-types-generator.d.ts +9 -0
- package/dist/utils/custom-types-generator.js +71 -0
- package/dist/utils/detect-schema-vendor.d.ts +22 -0
- package/dist/utils/detect-schema-vendor.js +76 -0
- package/dist/utils/ensure-function-metadata.d.ts +5 -2
- package/dist/utils/ensure-function-metadata.js +220 -6
- package/dist/utils/extract-function-name.d.ts +5 -16
- package/dist/utils/extract-function-name.js +86 -291
- package/dist/utils/extract-services.d.ts +2 -1
- package/dist/utils/extract-services.js +25 -1
- package/dist/utils/filter-inspector-state.js +107 -23
- package/dist/utils/get-property-value.d.ts +6 -1
- package/dist/utils/get-property-value.js +28 -3
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.js +23 -0
- package/dist/utils/middleware.d.ts +7 -30
- package/dist/utils/middleware.js +80 -66
- package/dist/utils/permissions.d.ts +2 -2
- package/dist/utils/permissions.js +10 -10
- package/dist/utils/post-process.d.ts +9 -10
- package/dist/utils/post-process.js +231 -24
- package/dist/utils/resolve-external-package.d.ts +12 -0
- package/dist/utils/resolve-external-package.js +34 -0
- package/dist/utils/resolve-function-types.d.ts +6 -0
- package/dist/utils/resolve-function-types.js +29 -0
- package/dist/utils/resolve-identifier.d.ts +10 -0
- package/dist/utils/resolve-identifier.js +36 -0
- package/dist/utils/resolve-versions.d.ts +2 -0
- package/dist/utils/resolve-versions.js +78 -0
- package/dist/utils/schema-generator.d.ts +9 -0
- package/dist/utils/schema-generator.js +209 -0
- package/dist/utils/serialize-inspector-state.d.ts +59 -22
- package/dist/utils/serialize-inspector-state.js +92 -20
- package/dist/utils/serialize-mcp-json.d.ts +2 -0
- package/dist/utils/serialize-mcp-json.js +99 -0
- package/dist/utils/serialize-middleware-groups-meta.d.ts +12 -0
- package/dist/utils/serialize-middleware-groups-meta.js +28 -0
- package/dist/utils/serialize-openapi-json.d.ts +85 -0
- package/dist/utils/serialize-openapi-json.js +151 -0
- package/dist/utils/serialize-permissions-groups-meta.d.ts +6 -0
- package/dist/utils/serialize-permissions-groups-meta.js +31 -0
- package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +34 -102
- package/dist/utils/workflow/dsl/extract-dsl-workflow.js +23 -4
- package/dist/utils/workflow/graph/convert-dsl-to-graph.js +12 -10
- package/dist/utils/workflow/graph/finalize-workflow-wires.d.ts +3 -0
- package/dist/utils/workflow/graph/finalize-workflow-wires.js +276 -0
- package/dist/utils/workflow/graph/finalize-workflows.d.ts +2 -0
- package/dist/utils/workflow/graph/finalize-workflows.js +75 -0
- package/dist/utils/workflow/graph/index.d.ts +2 -0
- package/dist/utils/workflow/graph/index.js +2 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +0 -8
- package/dist/utils/workflow/graph/serialize-workflow-graph.js +1 -3
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +53 -79
- package/dist/utils/workflow/graph/workflow-graph.types.js +1 -1
- package/dist/visit.js +11 -6
- package/package.json +14 -4
- package/src/add/add-ai-agent.ts +468 -0
- package/src/add/add-channel.ts +82 -79
- package/src/add/add-cli.ts +49 -20
- package/src/add/add-file-with-factory.ts +2 -0
- package/src/add/add-functions.ts +330 -86
- package/src/add/add-http-route.ts +245 -88
- package/src/add/add-http-routes.ts +228 -0
- package/src/add/add-keyed-wiring.ts +151 -0
- package/src/add/add-mcp-prompt.ts +26 -15
- package/src/add/add-mcp-resource.ts +27 -15
- package/src/add/add-middleware.ts +482 -80
- package/src/add/add-permission.ts +199 -40
- package/src/add/add-queue-worker.ts +24 -19
- package/src/add/add-rpc-invocations.ts +17 -0
- package/src/add/add-schedule.ts +16 -11
- package/src/add/add-secret.ts +140 -0
- package/src/add/add-trigger.ts +154 -0
- package/src/add/add-variable.ts +9 -0
- package/src/add/add-workflow-graph.ts +180 -522
- package/src/add/add-workflow.ts +5 -4
- package/src/error-codes.ts +24 -1
- package/src/index.ts +22 -13
- package/src/inspector.ts +129 -17
- package/src/schema-generator.ts +1 -0
- package/src/types-map.ts +12 -1
- package/src/types.ts +175 -58
- package/src/utils/compute-required-schemas.ts +49 -0
- package/src/utils/contract-hashes.test.ts +528 -0
- package/src/utils/contract-hashes.ts +290 -0
- package/src/utils/custom-types-generator.ts +88 -0
- package/src/utils/detect-schema-vendor.ts +90 -0
- package/src/utils/ensure-function-metadata.ts +324 -7
- package/src/utils/extract-function-name.ts +101 -351
- package/src/utils/extract-services.ts +35 -2
- package/src/utils/filter-inspector-state.test.ts +34 -20
- package/src/utils/filter-inspector-state.ts +140 -31
- package/src/utils/get-property-value.ts +42 -4
- package/src/utils/hash.ts +26 -0
- package/src/utils/middleware.test.ts +204 -0
- package/src/utils/middleware.ts +129 -67
- package/src/utils/permissions.test.ts +35 -12
- package/src/utils/permissions.ts +10 -10
- package/src/utils/post-process.ts +283 -43
- package/src/utils/resolve-external-package.ts +42 -0
- package/src/utils/resolve-function-types.ts +42 -0
- package/src/utils/resolve-identifier.ts +46 -0
- package/src/utils/resolve-versions.test.ts +249 -0
- package/src/utils/resolve-versions.ts +105 -0
- package/src/utils/schema-generator.ts +329 -0
- package/src/utils/serialize-inspector-state.ts +163 -40
- package/src/utils/serialize-mcp-json.ts +145 -0
- package/src/utils/serialize-middleware-groups-meta.ts +33 -0
- package/src/utils/serialize-openapi-json.ts +277 -0
- package/src/utils/serialize-permissions-groups-meta.ts +35 -0
- package/src/utils/test-data/inspector-state.json +69 -66
- package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +43 -119
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +24 -4
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +17 -10
- package/src/utils/workflow/graph/finalize-workflow-wires.ts +310 -0
- package/src/utils/workflow/graph/finalize-workflows.ts +100 -0
- package/src/utils/workflow/graph/index.ts +5 -0
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +1 -8
- package/src/utils/workflow/graph/workflow-graph.types.ts +29 -78
- package/src/visit.ts +12 -6
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/add/add-forge-credential.d.ts +0 -8
- package/dist/add/add-forge-credential.js +0 -77
- package/dist/add/add-forge-node.d.ts +0 -7
- package/dist/add/add-forge-node.js +0 -77
- package/dist/add/add-mcp-tool.d.ts +0 -2
- package/dist/add/add-mcp-tool.js +0 -81
- package/dist/utils/extract-service-metadata.d.ts +0 -19
- package/dist/utils/extract-service-metadata.js +0 -244
- package/dist/utils/write-service-metadata.d.ts +0 -13
- package/dist/utils/write-service-metadata.js +0 -37
- package/src/add/add-forge-credential.ts +0 -119
- package/src/add/add-forge-node.ts +0 -132
- package/src/add/add-mcp-tool.ts +0 -141
- package/src/utils/extract-service-metadata.ts +0 -353
- package/src/utils/write-service-metadata.ts +0 -51
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const PRIMITIVE_TYPES = new Set([
|
|
2
|
+
'boolean',
|
|
3
|
+
'string',
|
|
4
|
+
'number',
|
|
5
|
+
'null',
|
|
6
|
+
'undefined',
|
|
7
|
+
'void',
|
|
8
|
+
'any',
|
|
9
|
+
'unknown',
|
|
10
|
+
'never',
|
|
11
|
+
]);
|
|
12
|
+
export function computeRequiredSchemas(functionsMeta, typesMap, additionalTypes, schemaLookup) {
|
|
13
|
+
return new Set([
|
|
14
|
+
...Object.values(functionsMeta)
|
|
15
|
+
.map(({ inputs, outputs }) => {
|
|
16
|
+
const types = [];
|
|
17
|
+
if (inputs?.[0]) {
|
|
18
|
+
try {
|
|
19
|
+
types.push(typesMap.getUniqueName(inputs[0]));
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
types.push(inputs[0]);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (outputs?.[0]) {
|
|
26
|
+
try {
|
|
27
|
+
types.push(typesMap.getUniqueName(outputs[0]));
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
types.push(outputs[0]);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return types;
|
|
34
|
+
})
|
|
35
|
+
.flat()
|
|
36
|
+
.filter((s) => !!s && !PRIMITIVE_TYPES.has(s)),
|
|
37
|
+
...typesMap.customTypes.keys(),
|
|
38
|
+
...(additionalTypes || []),
|
|
39
|
+
...(schemaLookup ? Array.from(schemaLookup.keys()) : []),
|
|
40
|
+
]);
|
|
41
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { FunctionsMeta, JSONValue } from '@pikku/core';
|
|
2
|
+
import { TypesMap } from '../types-map.js';
|
|
3
|
+
import { ErrorCode } from '../error-codes.js';
|
|
4
|
+
export type ContractEntry = {
|
|
5
|
+
functionKey: string;
|
|
6
|
+
version: number;
|
|
7
|
+
contractHash: string;
|
|
8
|
+
};
|
|
9
|
+
export type VersionValidateError = {
|
|
10
|
+
code: ErrorCode;
|
|
11
|
+
message: string;
|
|
12
|
+
};
|
|
13
|
+
export type VersionManifestEntry = {
|
|
14
|
+
latest: number;
|
|
15
|
+
versions: Record<string, string>;
|
|
16
|
+
};
|
|
17
|
+
export type VersionManifest = {
|
|
18
|
+
manifestVersion: 1;
|
|
19
|
+
contracts: Record<string, VersionManifestEntry>;
|
|
20
|
+
};
|
|
21
|
+
export declare function createEmptyManifest(): VersionManifest;
|
|
22
|
+
export declare function serializeManifest(manifest: VersionManifest): string;
|
|
23
|
+
export declare function computeContractHash(data: {
|
|
24
|
+
functionKey: string;
|
|
25
|
+
inputSchema: unknown;
|
|
26
|
+
outputSchema: unknown;
|
|
27
|
+
}): string;
|
|
28
|
+
export declare function buildCurrentContracts(functionsMeta: FunctionsMeta, allSchemas: Record<string, JSONValue>, typesMap: TypesMap): Map<string, ContractEntry>;
|
|
29
|
+
export declare function computeContractHashes(allSchemas: Record<string, JSONValue>, typesMap: TypesMap, functionsMeta: FunctionsMeta): Map<string, ContractEntry>;
|
|
30
|
+
export declare function validateContracts(manifest: VersionManifest, currentContracts: Map<string, ContractEntry>): {
|
|
31
|
+
valid: boolean;
|
|
32
|
+
errors: VersionValidateError[];
|
|
33
|
+
};
|
|
34
|
+
export declare function updateManifest(existing: VersionManifest, currentContracts: Map<string, ContractEntry>): VersionManifest;
|
|
35
|
+
export declare function extractContractsFromMeta(functionsMeta: FunctionsMeta): Map<string, ContractEntry>;
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { parseVersionedId } from '@pikku/core';
|
|
2
|
+
import { ErrorCode } from '../error-codes.js';
|
|
3
|
+
import { canonicalJSON, hashString } from './hash.js';
|
|
4
|
+
export function createEmptyManifest() {
|
|
5
|
+
return {
|
|
6
|
+
manifestVersion: 1,
|
|
7
|
+
contracts: {},
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export function serializeManifest(manifest) {
|
|
11
|
+
const sortedContracts = {};
|
|
12
|
+
for (const key of Object.keys(manifest.contracts).sort()) {
|
|
13
|
+
const entry = manifest.contracts[key];
|
|
14
|
+
const sortedVersions = {};
|
|
15
|
+
const numericKeys = Object.keys(entry.versions)
|
|
16
|
+
.map(Number)
|
|
17
|
+
.sort((a, b) => a - b);
|
|
18
|
+
for (const vk of numericKeys) {
|
|
19
|
+
sortedVersions[String(vk)] = entry.versions[String(vk)];
|
|
20
|
+
}
|
|
21
|
+
sortedContracts[key] = {
|
|
22
|
+
latest: entry.latest,
|
|
23
|
+
versions: sortedVersions,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const sorted = {
|
|
27
|
+
manifestVersion: manifest.manifestVersion,
|
|
28
|
+
contracts: sortedContracts,
|
|
29
|
+
};
|
|
30
|
+
return JSON.stringify(sorted, null, 2) + '\n';
|
|
31
|
+
}
|
|
32
|
+
export function computeContractHash(data) {
|
|
33
|
+
return hashString(canonicalJSON(data), 16);
|
|
34
|
+
}
|
|
35
|
+
function resolveSchema(typeNames, allSchemas, typesMap) {
|
|
36
|
+
if (!typeNames) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const filtered = typeNames.filter((n) => n !== 'void');
|
|
40
|
+
if (filtered.length === 0) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const parts = [];
|
|
44
|
+
for (const name of filtered) {
|
|
45
|
+
let key;
|
|
46
|
+
try {
|
|
47
|
+
key = typesMap.getUniqueName(name);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
key = name;
|
|
51
|
+
}
|
|
52
|
+
const schema = allSchemas[key];
|
|
53
|
+
if (schema) {
|
|
54
|
+
parts.push(schema);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (parts.length === 0) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return parts.length === 1 ? parts[0] : parts;
|
|
61
|
+
}
|
|
62
|
+
export function buildCurrentContracts(functionsMeta, allSchemas, typesMap) {
|
|
63
|
+
const result = new Map();
|
|
64
|
+
for (const [funcId, meta] of Object.entries(functionsMeta)) {
|
|
65
|
+
if (meta.remote === true) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const parsed = parseVersionedId(funcId);
|
|
69
|
+
const functionKey = parsed.baseName;
|
|
70
|
+
const version = parsed.version ?? meta.version ?? 1;
|
|
71
|
+
const inputSchema = resolveSchema(meta.inputs, allSchemas, typesMap);
|
|
72
|
+
const outputSchema = resolveSchema(meta.outputs, allSchemas, typesMap);
|
|
73
|
+
const contractHash = computeContractHash({
|
|
74
|
+
functionKey,
|
|
75
|
+
inputSchema,
|
|
76
|
+
outputSchema,
|
|
77
|
+
});
|
|
78
|
+
result.set(funcId, { functionKey, version, contractHash });
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
export function computeContractHashes(allSchemas, typesMap, functionsMeta) {
|
|
83
|
+
const contracts = buildCurrentContracts(functionsMeta, allSchemas, typesMap);
|
|
84
|
+
for (const [funcId, entry] of contracts) {
|
|
85
|
+
const meta = functionsMeta[funcId];
|
|
86
|
+
if (meta) {
|
|
87
|
+
meta.contractHash = entry.contractHash;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return contracts;
|
|
91
|
+
}
|
|
92
|
+
function groupByFunctionKey(contracts) {
|
|
93
|
+
const grouped = new Map();
|
|
94
|
+
for (const entry of contracts.values()) {
|
|
95
|
+
const existing = grouped.get(entry.functionKey) ?? [];
|
|
96
|
+
existing.push(entry);
|
|
97
|
+
grouped.set(entry.functionKey, existing);
|
|
98
|
+
}
|
|
99
|
+
return grouped;
|
|
100
|
+
}
|
|
101
|
+
export function validateContracts(manifest, currentContracts) {
|
|
102
|
+
const errors = [];
|
|
103
|
+
const grouped = groupByFunctionKey(currentContracts);
|
|
104
|
+
const reportedKeys = new Set();
|
|
105
|
+
for (const [functionKey, entries] of grouped) {
|
|
106
|
+
const manifestEntry = manifest.contracts[functionKey];
|
|
107
|
+
if (!manifestEntry) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
for (const { version, contractHash } of entries) {
|
|
111
|
+
const existingHash = manifestEntry.versions[String(version)];
|
|
112
|
+
if (existingHash !== undefined) {
|
|
113
|
+
if (existingHash !== contractHash) {
|
|
114
|
+
reportedKeys.add(`${functionKey}@${version}`);
|
|
115
|
+
errors.push({
|
|
116
|
+
code: ErrorCode.FUNCTION_VERSION_MODIFIED,
|
|
117
|
+
message: `Contract for ${functionKey}@v${version} has changed (recorded: ${existingHash}, current: ${contractHash}). Existing versions are immutable.`,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
if (version <= manifestEntry.latest) {
|
|
123
|
+
errors.push({
|
|
124
|
+
code: ErrorCode.VERSION_REGRESSION_OR_CONFLICT,
|
|
125
|
+
message: `Version ${version} for ${functionKey} is <= latest (${manifestEntry.latest}) but not recorded. Possible merge conflict.`,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
else if (version > manifestEntry.latest + 1) {
|
|
129
|
+
errors.push({
|
|
130
|
+
code: ErrorCode.VERSION_GAP_NOT_ALLOWED,
|
|
131
|
+
message: `Version ${version} for ${functionKey} skips versions. Latest is ${manifestEntry.latest}, next must be ${manifestEntry.latest + 1}.`,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
for (const [functionKey, manifestEntry] of Object.entries(manifest.contracts)) {
|
|
138
|
+
const latestHash = manifestEntry.versions[String(manifestEntry.latest)];
|
|
139
|
+
const currentEntries = grouped.get(functionKey);
|
|
140
|
+
if (!currentEntries) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const currentLatest = currentEntries.find((e) => e.version === manifestEntry.latest);
|
|
144
|
+
if (currentLatest &&
|
|
145
|
+
currentLatest.contractHash !== latestHash &&
|
|
146
|
+
!reportedKeys.has(`${functionKey}@${manifestEntry.latest}`)) {
|
|
147
|
+
errors.push({
|
|
148
|
+
code: ErrorCode.CONTRACT_CHANGED_REQUIRES_BUMP,
|
|
149
|
+
message: `Contract for ${functionKey} changed. Set \`version: ${manifestEntry.latest + 1}\` on the function or run 'pikku versions update'.`,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
for (const [functionKey, manifestEntry] of Object.entries(manifest.contracts)) {
|
|
154
|
+
const numericKeys = Object.keys(manifestEntry.versions).map(Number);
|
|
155
|
+
if (numericKeys.length === 0) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const maxVersion = Math.max(...numericKeys);
|
|
159
|
+
if (manifestEntry.latest !== maxVersion) {
|
|
160
|
+
errors.push({
|
|
161
|
+
code: ErrorCode.MANIFEST_INTEGRITY_ERROR,
|
|
162
|
+
message: `Manifest integrity error for ${functionKey}: latest field (${manifestEntry.latest}) inconsistent with version keys (max: ${maxVersion}).`,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return { valid: errors.length === 0, errors };
|
|
167
|
+
}
|
|
168
|
+
export function updateManifest(existing, currentContracts) {
|
|
169
|
+
const manifest = {
|
|
170
|
+
manifestVersion: existing.manifestVersion,
|
|
171
|
+
contracts: JSON.parse(JSON.stringify(existing.contracts)),
|
|
172
|
+
};
|
|
173
|
+
const grouped = groupByFunctionKey(currentContracts);
|
|
174
|
+
for (const [functionKey, entries] of grouped) {
|
|
175
|
+
if (!manifest.contracts[functionKey]) {
|
|
176
|
+
manifest.contracts[functionKey] = { latest: 0, versions: {} };
|
|
177
|
+
}
|
|
178
|
+
const entry = manifest.contracts[functionKey];
|
|
179
|
+
for (const { version, contractHash } of entries) {
|
|
180
|
+
entry.versions[String(version)] = contractHash;
|
|
181
|
+
entry.latest = Math.max(entry.latest, version);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return manifest;
|
|
185
|
+
}
|
|
186
|
+
export function extractContractsFromMeta(functionsMeta) {
|
|
187
|
+
const result = new Map();
|
|
188
|
+
for (const [funcId, meta] of Object.entries(functionsMeta)) {
|
|
189
|
+
if (meta.remote === true || !meta.contractHash) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const parsed = parseVersionedId(funcId);
|
|
193
|
+
const functionKey = parsed.baseName;
|
|
194
|
+
const version = parsed.version ?? meta.version ?? 1;
|
|
195
|
+
result.set(funcId, {
|
|
196
|
+
functionKey,
|
|
197
|
+
version,
|
|
198
|
+
contractHash: meta.contractHash,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
return result;
|
|
202
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { TypesMap } from '../types-map.js';
|
|
2
|
+
/**
|
|
3
|
+
* NOTE: Code generation normally belongs in @pikku/cli, not the inspector.
|
|
4
|
+
* This is here because the schema generator needs the custom types content
|
|
5
|
+
* as a virtual TypeScript source file (in-memory, no disk write) so that
|
|
6
|
+
* ts-json-schema-generator can discover inline/custom types from typesMap.
|
|
7
|
+
*/
|
|
8
|
+
export declare function sanitizeTypeName(name: string): string;
|
|
9
|
+
export declare function generateCustomTypes(typesMap: TypesMap, requiredTypes: Set<string>): string;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NOTE: Code generation normally belongs in @pikku/cli, not the inspector.
|
|
3
|
+
* This is here because the schema generator needs the custom types content
|
|
4
|
+
* as a virtual TypeScript source file (in-memory, no disk write) so that
|
|
5
|
+
* ts-json-schema-generator can discover inline/custom types from typesMap.
|
|
6
|
+
*/
|
|
7
|
+
export function sanitizeTypeName(name) {
|
|
8
|
+
return name.replace(/[^a-zA-Z0-9_$]/g, '_');
|
|
9
|
+
}
|
|
10
|
+
export function generateCustomTypes(typesMap, requiredTypes) {
|
|
11
|
+
const typeDeclarations = Array.from(typesMap.customTypes.entries())
|
|
12
|
+
.filter(([_name, { type }]) => {
|
|
13
|
+
const hasUndefinedGeneric = /\b(Name|In|Out|Key)\b/.test(type) && /\[.*\]/.test(type);
|
|
14
|
+
return !hasUndefinedGeneric;
|
|
15
|
+
})
|
|
16
|
+
.map(([originalName, { type, references }]) => {
|
|
17
|
+
const name = sanitizeTypeName(originalName);
|
|
18
|
+
references.forEach((refName) => {
|
|
19
|
+
if (refName !== '__object' && !refName.startsWith('__object_')) {
|
|
20
|
+
requiredTypes.add(refName);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
const typeString = type;
|
|
24
|
+
const typeNameRegex = /\b[A-Z][a-zA-Z0-9]*\b/g;
|
|
25
|
+
const potentialTypes = typeString.match(typeNameRegex) || [];
|
|
26
|
+
potentialTypes.forEach((typeName) => {
|
|
27
|
+
if (typeString.includes(`"${typeName}"`) ||
|
|
28
|
+
[
|
|
29
|
+
'Pick',
|
|
30
|
+
'Omit',
|
|
31
|
+
'Partial',
|
|
32
|
+
'Required',
|
|
33
|
+
'Record',
|
|
34
|
+
'Readonly',
|
|
35
|
+
].includes(typeName)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const typeMeta = typesMap.getTypeMeta(typeName);
|
|
40
|
+
if (typeMeta.path) {
|
|
41
|
+
requiredTypes.add(typeMeta.originalName);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Type not found in map (ambient/builtin type)
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
if (name === type)
|
|
49
|
+
return null;
|
|
50
|
+
return `export type ${name} = ${type}`;
|
|
51
|
+
});
|
|
52
|
+
const importsByPath = new Map();
|
|
53
|
+
for (const typeName of requiredTypes) {
|
|
54
|
+
try {
|
|
55
|
+
const typeMeta = typesMap.getTypeMeta(typeName);
|
|
56
|
+
if (typeMeta.path) {
|
|
57
|
+
if (!importsByPath.has(typeMeta.path)) {
|
|
58
|
+
importsByPath.set(typeMeta.path, new Set());
|
|
59
|
+
}
|
|
60
|
+
importsByPath.get(typeMeta.path).add(typeMeta.originalName);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Type not found in map
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const importLines = Array.from(importsByPath.entries())
|
|
68
|
+
.map(([path, types]) => `import type { ${Array.from(types).join(', ')} } from '${path}'`)
|
|
69
|
+
.join('\n');
|
|
70
|
+
return `${importLines}\n\n${typeDeclarations.filter(Boolean).join('\n')}`;
|
|
71
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
import type { SchemaVendor, InspectorLogger } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Detect the schema vendor by tracing the type back to its library origin.
|
|
5
|
+
* This handles locally-defined schemas like `export const MySchema = z.object({...})`
|
|
6
|
+
* by checking where the type itself originates from.
|
|
7
|
+
*
|
|
8
|
+
* Supports multiple schema libraries in the same project (e.g., during migration
|
|
9
|
+
* from one library to another).
|
|
10
|
+
*/
|
|
11
|
+
export declare const detectSchemaVendor: (identifier: ts.Identifier, checker: ts.TypeChecker) => SchemaVendor;
|
|
12
|
+
/**
|
|
13
|
+
* Detect schema vendor and log a fatal error if unknown.
|
|
14
|
+
* Returns the vendor if successful, or undefined if unknown (after logging error).
|
|
15
|
+
*
|
|
16
|
+
* @param identifier - The TypeScript identifier for the schema variable
|
|
17
|
+
* @param checker - TypeScript type checker
|
|
18
|
+
* @param logger - Inspector logger for error reporting
|
|
19
|
+
* @param context - Description of what the schema is for (e.g., "Credential 'myCredential'")
|
|
20
|
+
* @param sourceFile - Source file path for error message
|
|
21
|
+
*/
|
|
22
|
+
export declare const detectSchemaVendorOrError: (identifier: ts.Identifier, checker: ts.TypeChecker, logger: InspectorLogger, context: string, sourceFile: string) => Exclude<SchemaVendor, "unknown"> | undefined;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { ErrorCode } from '../error-codes.js';
|
|
2
|
+
/**
|
|
3
|
+
* Detect the schema vendor by tracing the type back to its library origin.
|
|
4
|
+
* This handles locally-defined schemas like `export const MySchema = z.object({...})`
|
|
5
|
+
* by checking where the type itself originates from.
|
|
6
|
+
*
|
|
7
|
+
* Supports multiple schema libraries in the same project (e.g., during migration
|
|
8
|
+
* from one library to another).
|
|
9
|
+
*/
|
|
10
|
+
export const detectSchemaVendor = (identifier, checker) => {
|
|
11
|
+
const type = checker.getTypeAtLocation(identifier);
|
|
12
|
+
if (!type)
|
|
13
|
+
return 'unknown';
|
|
14
|
+
// Check the type's symbol declarations to find the library origin
|
|
15
|
+
const checkTypeOrigin = (t) => {
|
|
16
|
+
const symbol = t.getSymbol() || t.aliasSymbol;
|
|
17
|
+
if (symbol) {
|
|
18
|
+
const decls = symbol.getDeclarations();
|
|
19
|
+
if (decls) {
|
|
20
|
+
for (const decl of decls) {
|
|
21
|
+
const fileName = decl.getSourceFile().fileName;
|
|
22
|
+
if (fileName.includes('node_modules/zod'))
|
|
23
|
+
return 'zod';
|
|
24
|
+
if (fileName.includes('node_modules/valibot'))
|
|
25
|
+
return 'valibot';
|
|
26
|
+
if (fileName.includes('node_modules/arktype'))
|
|
27
|
+
return 'arktype';
|
|
28
|
+
if (fileName.includes('node_modules/@effect/schema'))
|
|
29
|
+
return 'effect';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Check base types for class/interface hierarchies
|
|
34
|
+
const baseTypes = t.getBaseTypes?.();
|
|
35
|
+
if (baseTypes) {
|
|
36
|
+
for (const baseType of baseTypes) {
|
|
37
|
+
const result = checkTypeOrigin(baseType);
|
|
38
|
+
if (result)
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
};
|
|
44
|
+
const vendor = checkTypeOrigin(type);
|
|
45
|
+
if (vendor)
|
|
46
|
+
return vendor;
|
|
47
|
+
// Fallback: check type arguments (for generic types like z.ZodObject<...>)
|
|
48
|
+
if (type.typeArguments) {
|
|
49
|
+
for (const arg of type.typeArguments || []) {
|
|
50
|
+
const result = checkTypeOrigin(arg);
|
|
51
|
+
if (result)
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return 'unknown';
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Detect schema vendor and log a fatal error if unknown.
|
|
59
|
+
* Returns the vendor if successful, or undefined if unknown (after logging error).
|
|
60
|
+
*
|
|
61
|
+
* @param identifier - The TypeScript identifier for the schema variable
|
|
62
|
+
* @param checker - TypeScript type checker
|
|
63
|
+
* @param logger - Inspector logger for error reporting
|
|
64
|
+
* @param context - Description of what the schema is for (e.g., "Credential 'myCredential'")
|
|
65
|
+
* @param sourceFile - Source file path for error message
|
|
66
|
+
*/
|
|
67
|
+
export const detectSchemaVendorOrError = (identifier, checker, logger, context, sourceFile) => {
|
|
68
|
+
const vendor = detectSchemaVendor(identifier, checker);
|
|
69
|
+
if (vendor === 'unknown') {
|
|
70
|
+
logger.critical(ErrorCode.INLINE_SCHEMA, `${context} schema vendor could not be determined from '${sourceFile}'. ` +
|
|
71
|
+
`Supported vendors: zod, valibot, arktype, @effect/schema. ` +
|
|
72
|
+
`Ensure your schema is imported from a supported validation library.`);
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
return vendor;
|
|
76
|
+
};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
1
2
|
import { InspectorState } from '../types.js';
|
|
2
3
|
/**
|
|
3
|
-
* Ensures that function metadata exists for a given
|
|
4
|
+
* Ensures that function metadata exists for a given pikkuFuncId.
|
|
4
5
|
* Creates stub metadata if it doesn't exist (useful for inline functions).
|
|
6
|
+
* When funcInitializer and checker are provided, resolves types and
|
|
7
|
+
* extracts tags/middleware/permissions from the pikkuFunc() config.
|
|
5
8
|
*/
|
|
6
|
-
export declare function ensureFunctionMetadata(state: InspectorState,
|
|
9
|
+
export declare function ensureFunctionMetadata(state: InspectorState, pikkuFuncId: string, fallbackName?: string, funcInitializer?: ts.Node, checker?: ts.TypeChecker, isHelper?: boolean): void;
|