@pikku/inspector 0.12.41 → 0.12.43
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 +104 -0
- package/dist/add/add-addon-bans.js +1 -0
- package/dist/add/add-ai-agent.d.ts +3 -1
- package/dist/add/add-ai-agent.js +82 -0
- package/dist/add/add-credential.js +3 -1
- package/dist/add/add-functions.js +10 -9
- package/dist/add/add-gateway.js +3 -0
- package/dist/add/add-http-route.js +2 -2
- package/dist/add/add-mcp-prompt.js +0 -1
- package/dist/add/add-mcp-resource.js +0 -1
- package/dist/add/add-permission.d.ts +1 -1
- package/dist/add/add-permission.js +3 -177
- package/dist/add/add-queue-worker.js +3 -0
- package/dist/add/add-schedule.js +3 -0
- package/dist/add/add-scope.d.ts +2 -0
- package/dist/add/add-scope.js +146 -0
- package/dist/add/add-trigger.js +3 -0
- package/dist/add/add-wire-remote-addon.d.ts +10 -0
- package/dist/add/add-wire-remote-addon.js +66 -0
- package/dist/add/add-workflow-graph.js +32 -3
- package/dist/error-codes.d.ts +3 -0
- package/dist/error-codes.js +4 -0
- package/dist/inspector.js +19 -7
- package/dist/types.d.ts +19 -13
- package/dist/utils/custom-types-generator.js +2 -1
- package/dist/utils/ensure-function-metadata.d.ts +15 -0
- package/dist/utils/ensure-function-metadata.js +24 -0
- package/dist/utils/filter-inspector-state.js +9 -0
- package/dist/utils/get-property-value.d.ts +7 -2
- package/dist/utils/get-property-value.js +48 -1
- package/dist/utils/load-addon-functions-meta.js +81 -6
- package/dist/utils/nested-project-filter.d.ts +1 -0
- package/dist/utils/nested-project-filter.js +29 -0
- package/dist/utils/permissions.d.ts +1 -21
- package/dist/utils/permissions.js +17 -108
- package/dist/utils/post-process.d.ts +30 -1
- package/dist/utils/post-process.js +150 -72
- package/dist/utils/serialize-inspector-state.d.ts +13 -8
- package/dist/utils/serialize-inspector-state.js +12 -6
- package/dist/utils/serialize-permissions-groups-meta.d.ts +0 -2
- package/dist/utils/serialize-permissions-groups-meta.js +0 -26
- package/dist/utils/workflow/derive-workflow-plan.js +9 -3
- package/dist/utils/workflow/dsl/extract-dsl-workflow.js +56 -1
- package/dist/utils/workflow/dsl/patterns.d.ts +10 -0
- package/dist/utils/workflow/dsl/patterns.js +24 -8
- package/dist/utils/workflow/dsl/validation.js +3 -4
- package/dist/utils/workflow/graph/convert-dsl-to-graph.js +14 -0
- package/dist/utils/workflow/graph/finalize-workflows.d.ts +7 -0
- package/dist/utils/workflow/graph/finalize-workflows.js +11 -2
- package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +2 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.js +4 -0
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +6 -2
- package/dist/visit.js +4 -0
- package/package.json +3 -3
- package/src/add/add-addon-bans.ts +1 -0
- package/src/add/add-ai-agent.test.ts +87 -0
- package/src/add/add-ai-agent.ts +122 -0
- package/src/add/add-credential.ts +6 -0
- package/src/add/add-functions.ts +10 -12
- package/src/add/add-gateway.ts +11 -0
- package/src/add/add-http-route.ts +2 -8
- package/src/add/add-mcp-prompt.ts +0 -1
- package/src/add/add-mcp-resource.ts +0 -1
- package/src/add/add-permission.ts +4 -242
- package/src/add/add-queue-worker.ts +11 -0
- package/src/add/add-schedule.ts +11 -0
- package/src/add/add-scope.test.ts +346 -0
- package/src/add/add-scope.ts +225 -0
- package/src/add/add-trigger.ts +11 -0
- package/src/add/add-wire-remote-addon.ts +77 -0
- package/src/add/add-workflow-graph-input.test.ts +94 -0
- package/src/add/add-workflow-graph-notes.test.ts +74 -0
- package/src/add/add-workflow-graph.ts +35 -3
- package/src/add/inline-wiring-function.test.ts +104 -0
- package/src/add/validate-workflow-graph-addons.test.ts +102 -0
- package/src/error-codes.ts +5 -0
- package/src/inspector.ts +22 -6
- package/src/types.ts +16 -17
- package/src/utils/custom-types-generator.test.ts +26 -1
- package/src/utils/custom-types-generator.ts +2 -1
- package/src/utils/ensure-function-metadata.ts +42 -0
- package/src/utils/filter-inspector-state.test.ts +0 -2
- package/src/utils/filter-inspector-state.ts +9 -0
- package/src/utils/get-property-value.test.ts +141 -0
- package/src/utils/get-property-value.ts +62 -1
- package/src/utils/load-addon-functions-meta.test.ts +157 -0
- package/src/utils/load-addon-functions-meta.ts +94 -6
- package/src/utils/load-addon-scopes.test.ts +138 -0
- package/src/utils/nested-project-filter.test.ts +53 -0
- package/src/utils/nested-project-filter.ts +28 -0
- package/src/utils/permissions.test.ts +7 -232
- package/src/utils/permissions.ts +20 -160
- package/src/utils/post-process.test.ts +269 -4
- package/src/utils/post-process.ts +216 -95
- package/src/utils/serialize-inspector-state.ts +22 -25
- package/src/utils/serialize-permissions-groups-meta.ts +0 -28
- package/src/utils/workflow/derive-workflow-plan.test.ts +41 -3
- package/src/utils/workflow/derive-workflow-plan.ts +11 -4
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +66 -0
- package/src/utils/workflow/dsl/patterns.ts +34 -8
- package/src/utils/workflow/dsl/validation.ts +3 -4
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +15 -0
- package/src/utils/workflow/graph/finalize-workflows.test.ts +50 -0
- package/src/utils/workflow/graph/finalize-workflows.ts +13 -2
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +6 -0
- package/src/utils/workflow/graph/workflow-graph.types.ts +8 -0
- package/src/visit.ts +4 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -4,12 +4,21 @@ import { extractStringLiteral } from './extract-node-value.js';
|
|
|
4
4
|
/**
|
|
5
5
|
* Extracts an array of strings from an object property.
|
|
6
6
|
*/
|
|
7
|
+
/**
|
|
8
|
+
* Unwraps `x as const` / `x as any` / `x satisfies T` down to the underlying
|
|
9
|
+
* expression. Without this a cast makes the value invisible to the inspector,
|
|
10
|
+
* so it is silently dropped from meta rather than validated — and `as const` on
|
|
11
|
+
* an array is idiomatic enough that this is easy to hit by accident.
|
|
12
|
+
*/
|
|
13
|
+
const unwrapAs = (node) => ts.isAsExpression(node) || ts.isSatisfiesExpression(node)
|
|
14
|
+
? unwrapAs(node.expression)
|
|
15
|
+
: node;
|
|
7
16
|
export const getArrayPropertyValue = (obj, propertyName) => {
|
|
8
17
|
const property = obj.properties.find((p) => ts.isPropertyAssignment(p) &&
|
|
9
18
|
ts.isIdentifier(p.name) &&
|
|
10
19
|
p.name.text === propertyName);
|
|
11
20
|
if (property && ts.isPropertyAssignment(property)) {
|
|
12
|
-
const initializer = property.initializer;
|
|
21
|
+
const initializer = unwrapAs(property.initializer);
|
|
13
22
|
if (ts.isArrayLiteralExpression(initializer)) {
|
|
14
23
|
return initializer.elements
|
|
15
24
|
.filter(ts.isStringLiteral)
|
|
@@ -18,6 +27,44 @@ export const getArrayPropertyValue = (obj, propertyName) => {
|
|
|
18
27
|
}
|
|
19
28
|
return null;
|
|
20
29
|
};
|
|
30
|
+
/**
|
|
31
|
+
* Extracts a string->string record from an object property.
|
|
32
|
+
*
|
|
33
|
+
* `getPropertyValue` falls through to `getText()` for an object literal, which
|
|
34
|
+
* returns the raw source text rather than the record — so callers that need the
|
|
35
|
+
* entries (e.g. oauth2 `additionalParams`) must use this instead.
|
|
36
|
+
*/
|
|
37
|
+
export const getRecordPropertyValue = (obj, propertyName) => {
|
|
38
|
+
const property = obj.properties.find((p) => ts.isPropertyAssignment(p) &&
|
|
39
|
+
ts.isIdentifier(p.name) &&
|
|
40
|
+
p.name.text === propertyName);
|
|
41
|
+
if (!property || !ts.isPropertyAssignment(property)) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const initializer = property.initializer;
|
|
45
|
+
if (!ts.isObjectLiteralExpression(initializer)) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const record = {};
|
|
49
|
+
for (const prop of initializer.properties) {
|
|
50
|
+
if (!ts.isPropertyAssignment(prop)) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const key = ts.isIdentifier(prop.name)
|
|
54
|
+
? prop.name.text
|
|
55
|
+
: ts.isStringLiteral(prop.name)
|
|
56
|
+
? prop.name.text
|
|
57
|
+
: null;
|
|
58
|
+
if (key === null) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (ts.isStringLiteral(prop.initializer) ||
|
|
62
|
+
ts.isNoSubstitutionTemplateLiteral(prop.initializer)) {
|
|
63
|
+
record[key] = prop.initializer.text;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return Object.keys(record).length > 0 ? record : null;
|
|
67
|
+
};
|
|
21
68
|
/**
|
|
22
69
|
* Wiring identity fields (`name`, `secretId`, `variableId`, …) are read
|
|
23
70
|
* STATICALLY from source — a const or variable reference is keyed by its
|
|
@@ -80,6 +80,11 @@ export async function loadAddonFunctionsMeta(logger, state) {
|
|
|
80
80
|
return;
|
|
81
81
|
const require = createRequire(join(state.rootDir, 'package.json'));
|
|
82
82
|
for (const [namespace, decl] of wireAddonDeclarations) {
|
|
83
|
+
// Remote addons (wireRemoteAddon) ship as a devDependency: types only.
|
|
84
|
+
// Their functions, secrets, variables, schemas and services live on the
|
|
85
|
+
// HOST that runs them — never load or require any of that here.
|
|
86
|
+
if (decl.remote)
|
|
87
|
+
continue;
|
|
83
88
|
try {
|
|
84
89
|
const metaPath = require.resolve(`${decl.package}/.pikku/function/pikku-functions-meta.gen.json`);
|
|
85
90
|
const raw = await readFile(metaPath, 'utf-8');
|
|
@@ -108,32 +113,99 @@ export async function loadAddonFunctionsMeta(logger, state) {
|
|
|
108
113
|
const secretsRaw = await readFile(secretsMetaPath, 'utf-8');
|
|
109
114
|
const secretsMeta = JSON.parse(secretsRaw);
|
|
110
115
|
for (const [key, def] of Object.entries(secretsMeta)) {
|
|
111
|
-
|
|
116
|
+
// secretOverrides key on the SECRET ID (the string the addon passes to
|
|
117
|
+
// getSecret — its typed map is keyed by secretId, e.g.
|
|
118
|
+
// `getSecret('MAILGUN_CREDENTIALS')`), NOT the logical meta key, so the
|
|
119
|
+
// runtime aliaser (which also keys on secretId) and this merge agree.
|
|
120
|
+
// The resolved id is the real project secret; the addon's secretId is
|
|
121
|
+
// the default when no override is given. Two instances with different
|
|
122
|
+
// overrides therefore surface two distinct project secrets. `key` is
|
|
123
|
+
// the fallback for older meta that predates the secretId field.
|
|
124
|
+
const secretId = def.secretId ?? key;
|
|
125
|
+
const resolvedSecretId = decl.secretOverrides?.[secretId] ?? secretId;
|
|
126
|
+
const existing = state.secrets.definitions.find((d) => (d.secretId ?? d.name) === resolvedSecretId);
|
|
112
127
|
if (!existing) {
|
|
113
|
-
state.secrets.definitions.push(
|
|
114
|
-
|
|
128
|
+
state.secrets.definitions.push({
|
|
129
|
+
...def,
|
|
130
|
+
secretId: resolvedSecretId,
|
|
131
|
+
});
|
|
132
|
+
logger.debug(`Loaded addon secret '${resolvedSecretId}' from ${decl.package}`);
|
|
115
133
|
}
|
|
116
134
|
}
|
|
117
135
|
}
|
|
118
136
|
catch {
|
|
119
137
|
// No secrets meta — that's fine
|
|
120
138
|
}
|
|
139
|
+
// Load addon scopes meta. Without this an addon's own scopes never reach
|
|
140
|
+
// the host's ScopeId union or its declared set, so the pikku_scopes FK
|
|
141
|
+
// would refuse to grant one.
|
|
142
|
+
try {
|
|
143
|
+
const scopesMetaPath = require.resolve(`${decl.package}/.pikku/scopes/pikku-scopes-meta.gen.json`);
|
|
144
|
+
const scopesRaw = await readFile(scopesMetaPath, 'utf-8');
|
|
145
|
+
const scopesMeta = JSON.parse(scopesRaw);
|
|
146
|
+
for (const [key, def] of Object.entries(scopesMeta)) {
|
|
147
|
+
const existing = state.scopes.definitions.find((d) => d.name === key);
|
|
148
|
+
if (!existing) {
|
|
149
|
+
state.scopes.definitions.push(def);
|
|
150
|
+
logger.debug(`Loaded addon scope '${key}' from ${decl.package}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
// No scopes meta — that's fine
|
|
156
|
+
}
|
|
121
157
|
// Load addon variables meta
|
|
122
158
|
try {
|
|
123
159
|
const variablesMetaPath = require.resolve(`${decl.package}/.pikku/variables/pikku-variables-meta.gen.json`);
|
|
124
160
|
const variablesRaw = await readFile(variablesMetaPath, 'utf-8');
|
|
125
161
|
const variablesMeta = JSON.parse(variablesRaw);
|
|
126
162
|
for (const [key, def] of Object.entries(variablesMeta)) {
|
|
127
|
-
|
|
163
|
+
// variableOverrides key on the VARIABLE ID (the string the addon reads
|
|
164
|
+
// via its typed map, keyed by variableId), same as secretOverrides key
|
|
165
|
+
// on secretId — so the runtime aliaser and this merge agree. `key` is
|
|
166
|
+
// the fallback for older meta without a variableId field.
|
|
167
|
+
const variableId = def.variableId ?? key;
|
|
168
|
+
const resolvedVariableId = decl.variableOverrides?.[variableId] ?? variableId;
|
|
169
|
+
const existing = state.variables.definitions.find((d) => (d.variableId ?? d.name) === resolvedVariableId);
|
|
128
170
|
if (!existing) {
|
|
129
|
-
state.variables.definitions.push(
|
|
130
|
-
|
|
171
|
+
state.variables.definitions.push({
|
|
172
|
+
...def,
|
|
173
|
+
variableId: resolvedVariableId,
|
|
174
|
+
});
|
|
175
|
+
logger.debug(`Loaded addon variable '${resolvedVariableId}' from ${decl.package}`);
|
|
131
176
|
}
|
|
132
177
|
}
|
|
133
178
|
}
|
|
134
179
|
catch {
|
|
135
180
|
// No variables meta — that's fine
|
|
136
181
|
}
|
|
182
|
+
// Load addon credentials meta. Without this an addon's OAuth2/wire
|
|
183
|
+
// credentials never reach the consuming app's CREDENTIAL_OAUTH2_CONFIGS,
|
|
184
|
+
// so the credential-oauth provider and BetterAuthCredentialService can't
|
|
185
|
+
// resolve them — the addon's Connect flow and status silently no-op.
|
|
186
|
+
try {
|
|
187
|
+
const credentialsMetaPath = require.resolve(`${decl.package}/.pikku/credentials/pikku-credentials-meta.gen.json`);
|
|
188
|
+
const credentialsRaw = await readFile(credentialsMetaPath, 'utf-8');
|
|
189
|
+
const credentialsMeta = JSON.parse(credentialsRaw);
|
|
190
|
+
for (const [key, def] of Object.entries(credentialsMeta)) {
|
|
191
|
+
// Each instance's credentialOverrides remap the addon's logical
|
|
192
|
+
// credential name to a project credential — same mechanic as secrets
|
|
193
|
+
// and variables above. The credential name doubles as the better-auth
|
|
194
|
+
// providerId (accounts keyed by providerId+userId), so two instances
|
|
195
|
+
// with different overrides surface two distinct OAuth providers rather
|
|
196
|
+
// than sharing one account pool. The logical name is the default when
|
|
197
|
+
// no override is given.
|
|
198
|
+
const resolvedName = decl.credentialOverrides?.[key] ?? key;
|
|
199
|
+
const existing = state.credentials.definitions.find((d) => d.name === resolvedName);
|
|
200
|
+
if (!existing) {
|
|
201
|
+
state.credentials.definitions.push({ ...def, name: resolvedName });
|
|
202
|
+
logger.debug(`Loaded addon credential '${resolvedName}' from ${decl.package}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
// No credentials meta — that's fine
|
|
208
|
+
}
|
|
137
209
|
// Load addon serverlessIncompatible service names from pikku-addon-meta.gen.json
|
|
138
210
|
let loadedParentServices = false;
|
|
139
211
|
try {
|
|
@@ -215,6 +287,9 @@ export async function loadAddonSchemas(logger, state) {
|
|
|
215
287
|
return;
|
|
216
288
|
const require = createRequire(join(state.rootDir, 'package.json'));
|
|
217
289
|
for (const [namespace, decl] of wireAddonDeclarations) {
|
|
290
|
+
// Remote addons carry no local schemas — their funcs run on the host.
|
|
291
|
+
if (decl.remote)
|
|
292
|
+
continue;
|
|
218
293
|
try {
|
|
219
294
|
const metaPath = require.resolve(`${decl.package}/.pikku/function/pikku-functions-meta.gen.json`);
|
|
220
295
|
const schemasDir = join(dirname(metaPath), '..', 'schemas', 'schemas');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const createNestedProjectFilter: (rootDir: string) => (fileName: string) => boolean;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { dirname, join } from 'path';
|
|
3
|
+
export const createNestedProjectFilter = (rootDir) => {
|
|
4
|
+
const cache = new Map();
|
|
5
|
+
return (fileName) => {
|
|
6
|
+
const pending = [];
|
|
7
|
+
let dir = dirname(fileName);
|
|
8
|
+
let nested = false;
|
|
9
|
+
while (dir.startsWith(rootDir) && dir !== rootDir) {
|
|
10
|
+
const cached = cache.get(dir);
|
|
11
|
+
if (cached !== undefined) {
|
|
12
|
+
nested = cached;
|
|
13
|
+
break;
|
|
14
|
+
}
|
|
15
|
+
pending.push(dir);
|
|
16
|
+
if (existsSync(join(dir, 'pikku.config.json'))) {
|
|
17
|
+
nested = true;
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
const parent = dirname(dir);
|
|
21
|
+
if (parent === dir)
|
|
22
|
+
break;
|
|
23
|
+
dir = parent;
|
|
24
|
+
}
|
|
25
|
+
for (const d of pending)
|
|
26
|
+
cache.set(d, nested);
|
|
27
|
+
return nested;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
@@ -16,28 +16,8 @@ export declare function extractPermissionPikkuNames(node: ts.Expression, checker
|
|
|
16
16
|
* Returns the initializer node for the 'permissions' property if it exists
|
|
17
17
|
*/
|
|
18
18
|
export declare function getPermissionsNode(obj: ts.ObjectLiteralExpression): ts.Expression | undefined;
|
|
19
|
-
/**
|
|
20
|
-
* Check if a route matches a pattern with wildcards
|
|
21
|
-
* Pattern can be exact match or use * as wildcard
|
|
22
|
-
* e.g., '/api/*' matches '/api/users', '/api/posts/123', etc.
|
|
23
|
-
*/
|
|
24
|
-
export declare function routeMatchesPattern(route: string, pattern: string): boolean;
|
|
25
|
-
/**
|
|
26
|
-
* Resolve permissions for an HTTP wiring based on:
|
|
27
|
-
* 1. Global HTTP permissions (addHTTPPermission('*', [...]))
|
|
28
|
-
* 2. Route-specific HTTP permissions (addHTTPPermission('/pattern', [...]))
|
|
29
|
-
* 3. Tag-based permissions (addPermission('tag', [...]))
|
|
30
|
-
* 4. Explicit wiring permissions (wireHTTP({ permissions: [...] }))
|
|
31
|
-
* Returns undefined if no permissions are found, otherwise returns array with at least one item
|
|
32
|
-
*/
|
|
33
|
-
export declare function resolveHTTPPermissions(state: InspectorState, route: string, tags: string[] | undefined, explicitPermissionsNode: ts.Expression | undefined, checker: ts.TypeChecker): PermissionMetadata[] | undefined;
|
|
34
19
|
/**
|
|
35
20
|
* Convenience wrapper: Extract permissions node from object and resolve
|
|
36
21
|
* Use this in add-* files for cleaner code
|
|
37
22
|
*/
|
|
38
|
-
export declare function resolvePermissions(state: InspectorState, obj: ts.ObjectLiteralExpression,
|
|
39
|
-
/**
|
|
40
|
-
* Convenience wrapper for HTTP: Extract permissions and resolve with HTTP-specific logic
|
|
41
|
-
* Use this in add-http-route.ts for cleaner code
|
|
42
|
-
*/
|
|
43
|
-
export declare function resolveHTTPPermissionsFromObject(state: InspectorState, route: string, obj: ts.ObjectLiteralExpression, tags: string[] | undefined, checker: ts.TypeChecker): PermissionMetadata[] | undefined;
|
|
23
|
+
export declare function resolvePermissions(state: InspectorState, obj: ts.ObjectLiteralExpression, _tags: string[] | undefined, checker: ts.TypeChecker): PermissionMetadata[] | undefined;
|
|
@@ -57,122 +57,31 @@ export function getPermissionsNode(obj) {
|
|
|
57
57
|
return permissionsProp?.initializer;
|
|
58
58
|
}
|
|
59
59
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
60
|
+
* Resolve the explicit permission references declared on a function (or agent).
|
|
61
|
+
* Permissions are function-scoped only: a `permissions: { group: [fn] }` object
|
|
62
|
+
* is flattened to `{ type: 'wire', name }` references used at filter time.
|
|
63
|
+
* Returns undefined if none are declared, otherwise an array with ≥1 item.
|
|
63
64
|
*/
|
|
64
|
-
|
|
65
|
-
if (
|
|
66
|
-
return
|
|
67
|
-
// Convert pattern to regex: replace * with .*
|
|
68
|
-
const regexPattern = pattern
|
|
69
|
-
.replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Escape regex special chars except *
|
|
70
|
-
.replace(/\*/g, '.*'); // Replace * with .*
|
|
71
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
72
|
-
return regex.test(route);
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Resolve permissions for an HTTP wiring based on:
|
|
76
|
-
* 1. Global HTTP permissions (addHTTPPermission('*', [...]))
|
|
77
|
-
* 2. Route-specific HTTP permissions (addHTTPPermission('/pattern', [...]))
|
|
78
|
-
* 3. Tag-based permissions (addPermission('tag', [...]))
|
|
79
|
-
* 4. Explicit wiring permissions (wireHTTP({ permissions: [...] }))
|
|
80
|
-
* Returns undefined if no permissions are found, otherwise returns array with at least one item
|
|
81
|
-
*/
|
|
82
|
-
export function resolveHTTPPermissions(state, route, tags, explicitPermissionsNode, checker) {
|
|
83
|
-
const resolved = [];
|
|
84
|
-
// 1. HTTP route permission groups (includes '*' for global)
|
|
85
|
-
for (const [pattern, _groupMeta] of state.http.routePermissions.entries()) {
|
|
86
|
-
if (routeMatchesPattern(route, pattern)) {
|
|
87
|
-
// Just reference the group by route pattern
|
|
88
|
-
resolved.push({
|
|
89
|
-
type: 'http',
|
|
90
|
-
route: pattern,
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
// 2. Tag-based permission groups
|
|
95
|
-
if (tags && tags.length > 0) {
|
|
96
|
-
for (const tag of tags) {
|
|
97
|
-
if (state.permissions.tagPermissions.has(tag)) {
|
|
98
|
-
// Just reference the group by tag
|
|
99
|
-
resolved.push({
|
|
100
|
-
type: 'tag',
|
|
101
|
-
tag,
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
// 3. Explicit wire permissions (inline is OK here)
|
|
107
|
-
if (explicitPermissionsNode) {
|
|
108
|
-
const permissionNames = extractPermissionPikkuNames(explicitPermissionsNode, checker, state.rootDir);
|
|
109
|
-
for (const name of permissionNames) {
|
|
110
|
-
const def = state.permissions.definitions[name];
|
|
111
|
-
resolved.push({
|
|
112
|
-
type: 'wire',
|
|
113
|
-
name,
|
|
114
|
-
inline: def?.exportedName === null,
|
|
115
|
-
});
|
|
116
|
-
}
|
|
65
|
+
function resolveExplicitPermissions(state, explicitPermissionsNode, checker) {
|
|
66
|
+
if (!explicitPermissionsNode) {
|
|
67
|
+
return undefined;
|
|
117
68
|
}
|
|
118
|
-
return resolved.length > 0 ? resolved : undefined;
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* Resolve tag-based and explicit permissions (common logic for wires and functions)
|
|
122
|
-
* 1. Tag-based permissions (addPermission('tag', [...]))
|
|
123
|
-
* 2. Explicit permissions (wireHTTP/pikkuFunc({ permissions: [...] }))
|
|
124
|
-
*/
|
|
125
|
-
function resolveTagAndExplicitPermissions(state, tags, explicitPermissionsNode, checker) {
|
|
126
69
|
const resolved = [];
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
// 2. Explicit permissions (inline is OK here - used directly in wire/function)
|
|
140
|
-
if (explicitPermissionsNode) {
|
|
141
|
-
const permissionNames = extractPermissionPikkuNames(explicitPermissionsNode, checker, state.rootDir);
|
|
142
|
-
for (const name of permissionNames) {
|
|
143
|
-
const def = state.permissions.definitions[name];
|
|
144
|
-
resolved.push({
|
|
145
|
-
type: 'wire',
|
|
146
|
-
name,
|
|
147
|
-
inline: def?.exportedName === null,
|
|
148
|
-
});
|
|
149
|
-
}
|
|
70
|
+
const permissionNames = extractPermissionPikkuNames(explicitPermissionsNode, checker, state.rootDir);
|
|
71
|
+
for (const name of permissionNames) {
|
|
72
|
+
const def = state.permissions.definitions[name];
|
|
73
|
+
resolved.push({
|
|
74
|
+
type: 'wire',
|
|
75
|
+
name,
|
|
76
|
+
inline: def?.exportedName === null,
|
|
77
|
+
});
|
|
150
78
|
}
|
|
151
|
-
return resolved;
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Resolve permissions for a function based on:
|
|
155
|
-
* 1. Tag-based permissions (addPermission('tag', [...]))
|
|
156
|
-
* 2. Explicit function permissions (pikkuFunc({ permissions: [...] }))
|
|
157
|
-
* Returns undefined if no permissions are found, otherwise returns array with at least one item
|
|
158
|
-
*/
|
|
159
|
-
function resolveFunctionPermissionsInternal(state, tags, explicitPermissionsNode, checker) {
|
|
160
|
-
const resolved = resolveTagAndExplicitPermissions(state, tags, explicitPermissionsNode, checker);
|
|
161
79
|
return resolved.length > 0 ? resolved : undefined;
|
|
162
80
|
}
|
|
163
81
|
/**
|
|
164
82
|
* Convenience wrapper: Extract permissions node from object and resolve
|
|
165
83
|
* Use this in add-* files for cleaner code
|
|
166
84
|
*/
|
|
167
|
-
export function resolvePermissions(state, obj,
|
|
168
|
-
|
|
169
|
-
return resolveFunctionPermissionsInternal(state, tags, explicitPermissionsNode, checker);
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Convenience wrapper for HTTP: Extract permissions and resolve with HTTP-specific logic
|
|
173
|
-
* Use this in add-http-route.ts for cleaner code
|
|
174
|
-
*/
|
|
175
|
-
export function resolveHTTPPermissionsFromObject(state, route, obj, tags, checker) {
|
|
176
|
-
const explicitPermissionsNode = getPermissionsNode(obj);
|
|
177
|
-
return resolveHTTPPermissions(state, route, tags, explicitPermissionsNode, checker);
|
|
85
|
+
export function resolvePermissions(state, obj, _tags, checker) {
|
|
86
|
+
return resolveExplicitPermissions(state, getPermissionsNode(obj), checker);
|
|
178
87
|
}
|
|
@@ -19,7 +19,7 @@ export declare function stampAuthHandlerServices(state: InspectorState | Omit<In
|
|
|
19
19
|
* Only extracts type:'wire' variants (individual middleware/permissions).
|
|
20
20
|
* Skips type:'http' and type:'tag' (reference groups, not individuals).
|
|
21
21
|
*/
|
|
22
|
-
export declare function extractWireNames(list?: MiddlewareMetadata
|
|
22
|
+
export declare function extractWireNames(list?: Array<MiddlewareMetadata | PermissionMetadata>): string[];
|
|
23
23
|
/**
|
|
24
24
|
* Aggregates all required services from wired functions, middleware, and permissions.
|
|
25
25
|
* Must be called after AST traversal completes.
|
|
@@ -31,11 +31,32 @@ export declare function aggregateRequiredServices(state: InspectorState | Omit<I
|
|
|
31
31
|
export declare function validateSecretOverrides(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
|
|
32
32
|
export declare function validateCredentialOverrides(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
|
|
33
33
|
export declare function validateVariableOverrides(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
|
|
34
|
+
/**
|
|
35
|
+
* A `wireRemoteAddon` package ships types only — its handlers run on the host —
|
|
36
|
+
* so it MUST be a devDependency, not a production dependency (a prod dep would
|
|
37
|
+
* drag in the runtime deps remote consumption exists to avoid). This is the
|
|
38
|
+
* mirror image of `wireAddon`, which requires a production dependency.
|
|
39
|
+
*/
|
|
40
|
+
export declare function validateRemoteAddonDependencies(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
|
|
41
|
+
/**
|
|
42
|
+
* The auth a `wireRemoteAddon` consumer binds must reference a slot the consumer
|
|
43
|
+
* actually declares: a `credentialId` must be a wired credential, a `secretId`
|
|
44
|
+
* a wired secret. A custom `resolve()` and a public (omitted) surface are not
|
|
45
|
+
* statically checkable and are left to runtime.
|
|
46
|
+
*/
|
|
47
|
+
export declare function validateRemoteAddonAuth(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
|
|
34
48
|
export declare function computeResolvedIOTypes(state: InspectorState): void;
|
|
35
49
|
export declare function computeMiddlewareGroupsMeta(state: InspectorState): void;
|
|
36
50
|
export declare function computePermissionsGroupsMeta(state: InspectorState): void;
|
|
37
51
|
export declare function computeRequiredSchemas(state: InspectorState, options: InspectorOptions): void;
|
|
38
52
|
export declare function validateAgentModels(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
|
|
53
|
+
/**
|
|
54
|
+
* A `pikkuWorkflowGraph` node that references the `graph:` namespace (e.g.
|
|
55
|
+
* `graph:editFields`) needs @pikku/addon-graph wired — otherwise the RPC never
|
|
56
|
+
* registers and codegen fails deep in type-checking with an opaque error. Fail
|
|
57
|
+
* early with an actionable message pointing at `scaffold: { graph: true }`.
|
|
58
|
+
*/
|
|
59
|
+
export declare function validateWorkflowGraphAddons(logger: InspectorLogger, state: InspectorState): void;
|
|
39
60
|
/**
|
|
40
61
|
* Validates that Zod schemas and wiring side-effects (wireHTTPRoutes,
|
|
41
62
|
* addPermission, addHTTPMiddleware, etc.) do not coexist in the same file.
|
|
@@ -46,3 +67,11 @@ export declare function validateAgentModels(logger: InspectorLogger, state: Insp
|
|
|
46
67
|
*/
|
|
47
68
|
export declare function validateSchemaWiringSeparation(logger: InspectorLogger, state: InspectorState): void;
|
|
48
69
|
export declare function computeDiagnostics(state: InspectorState): void;
|
|
70
|
+
/**
|
|
71
|
+
* Validates that every scope referenced by a function is declared via
|
|
72
|
+
* `wireScope`. Runs after all visitors, so declaration order does not matter.
|
|
73
|
+
*
|
|
74
|
+
* A `*` suffix is a wildcard requirement: `admin:*` requires the `admin` scope
|
|
75
|
+
* to be declared, and grants its whole subtree.
|
|
76
|
+
*/
|
|
77
|
+
export declare function validateScopeReferences(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
|