@pikku/inspector 0.12.81 → 0.12.83
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 +79 -0
- package/dist/add/add-file-with-factory.js +35 -8
- package/dist/add/add-middleware.js +66 -42
- package/dist/add/add-wire-addon.js +55 -5
- 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.js +3 -1
- package/dist/types.d.ts +25 -2
- package/dist/utils/load-addon-functions-meta.js +24 -11
- package/dist/utils/post-process.d.ts +6 -0
- package/dist/utils/post-process.js +40 -3
- package/dist/utils/resolve-addon-package.d.ts +2 -1
- package/dist/utils/serialize-inspector-state.d.ts +3 -1
- package/dist/utils/serialize-inspector-state.js +2 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,82 @@
|
|
|
1
|
+
## 0.12.83
|
|
2
|
+
|
|
3
|
+
### Patch Changes
|
|
4
|
+
|
|
5
|
+
- 6db6a14: A wiring can now decide whether an addon's credential is per-user or deployment-wide
|
|
6
|
+
|
|
7
|
+
`wireAddon`'s `credentialOverrides` takes an object as well as a rename string:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
wireAddon({
|
|
11
|
+
name: 'gmail',
|
|
12
|
+
package: '@pikku/addon-gmail',
|
|
13
|
+
credentialOverrides: {
|
|
14
|
+
gmailOAuth: { mode: 'wire' }, // each user connects their own
|
|
15
|
+
calendarOAuth: { name: 'CAL', mode: 'singleton' },
|
|
16
|
+
},
|
|
17
|
+
})
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
An addon author declares a default with `defineCredential`; the deployment decides, so one addon serves both a per-user product and a single team account.
|
|
21
|
+
|
|
22
|
+
The wire credential service now resolves each credential by what it _is_ rather than by what a lookup returned: `wire` reads only the user's value, `singleton` reads the deployment's. A per-user credential can no longer pick up a platform-level value because the user has not connected — which, before, would have quietly run someone's request against the deployment's own account.
|
|
23
|
+
|
|
24
|
+
A credential is never read from the secret vault. The wire credential service no longer takes a `SecretService` at all, so the only way a credential arrives is the one its mode names — the user's own value, or the deployment's.
|
|
25
|
+
|
|
26
|
+
Modes are resolved at generation time into the credentials meta, so the console's connect flow reflects the wiring rather than the addon's default.
|
|
27
|
+
|
|
28
|
+
Two credentials that resolve to one name are rejected unless they agree on the mode. Generation writes a single metadata entry per name, so a `wire` credential aliased onto a `singleton` one used to take whichever wiring was read last — a per-user credential served from the deployment's own account, or a deployment credential handed out per user. The inspector reports the collision against both wirings by name, and `buildCredentialResolutions` refuses it at runtime too.
|
|
29
|
+
|
|
30
|
+
A mode-only override is checked against the credential it names. `{ mode: 'wire' }` renames nothing, so nothing was validated: an override naming a credential that does not exist was dropped in silence and the credential kept the mode its author declared.
|
|
31
|
+
|
|
32
|
+
A credential the wiring resolves as `singleton` is no longer read out of the user's own store. Per-user values are imported first and a name already present is left alone, so a value stored under a singleton's name — from an earlier wiring, or a connect flow since rewired — decided what the deployment's own slot resolved to. It is now skipped by mode, the same way a `wire` credential is kept away from the deployment's value.
|
|
33
|
+
|
|
34
|
+
The project's own credentials are registered into pikku state from the generated credentials file, so `wire.getCredential` resolves an app-level singleton the same way it resolves an addon's.
|
|
35
|
+
|
|
36
|
+
`pikku new addon` no longer requires a `pikku.config.json` in the working directory. Scaffolding an addon is something you do before a project config exists, so the command now reads one when it is there and falls back to the working directory when it is not.
|
|
37
|
+
|
|
38
|
+
An addon's `pikkuAddonWireServices` factory now declares the same service contract its singleton factory does: what it destructures off the parent's bag is required, and what it returns is built by the addon. Before, a service an addon built per wire was demanded from the consumer, and a wire-only addon declared no contract at all. A nested callback that names its own parameter after the factory's is no longer read as the factory's own: what it destructured went onto the addon's contract, so a consumer was asked for a service the addon never wanted from them.
|
|
39
|
+
|
|
40
|
+
An OAuth2 credential now implies its app secret, so nobody hand-writes one. The client id and secret an OAuth app needs is the same shape every time — `OAuth2AppCredential`, which is what the runtime already reads it as — so the inspector registers a secret for each credential's `appCredentialSecretId`. A hand-written `defineSecret` covering that id still wins, so an author who wants their own description or `docsUrl` keeps it. The derived secret is optional, matching what every hand-written declaration chose: an addon that also authenticates by API key must still deploy without an OAuth app configured.
|
|
41
|
+
|
|
42
|
+
`optional` now means a secret is not reported as missing. `getMissing()` filtered on "not configured" alone, so a secret whose declaration said absence was supported still showed up on the list of things a deployment had to go and supply — burying the ones that genuinely were. `getAllStatus()` still reports it, flagged `optional`.
|
|
43
|
+
|
|
44
|
+
An OAuth2 secret carries its declaration's `optional` through code generation, to the app credential and to the token store alike: nobody connects without the client id and secret, so a deployment allowed to omit the app is never asked for its tokens either. That branch never looked at the flag before.
|
|
45
|
+
|
|
46
|
+
`credentialOAuthProviders` treats an app secret that resolves `undefined` as unconfigured. An optional secret resolves rather than throws, so the absence arrived past the `catch` that was meant to skip the provider — and `.reveal()` on it threw a `TypeError` that took every `getSession` down with it, which is the exact failure that code exists to prevent.
|
|
47
|
+
|
|
48
|
+
- Updated dependencies [cb8f57e]
|
|
49
|
+
- Updated dependencies [cb8f57e]
|
|
50
|
+
- Updated dependencies [6db6a14]
|
|
51
|
+
- @pikku/core@0.12.114
|
|
52
|
+
|
|
53
|
+
## 0.12.82
|
|
54
|
+
|
|
55
|
+
### Patch Changes
|
|
56
|
+
|
|
57
|
+
- c842054: Middleware instance ids are now allocated per group across the whole inspection, not per source file, and a build that would drop a global middleware registration fails instead of shipping.
|
|
58
|
+
|
|
59
|
+
The per-group index restarted at 0 for every file, so the second file to register against a group minted ids the first already owned and overwrote its entries in `middleware.instances` — `instanceIds` listed one key twice while `count` correctly said two. For `addGlobalMiddleware` that was not cosmetic. Global middleware belongs to no wire group, so the instance map is the only record that its module must be imported, and per-unit deploy codegen emits its side-effect imports from that map. An app file registering a global middleware was erased by the generated auth scaffold registering its own, the app's module was never imported, its registration never ran — and because auth commonly lives in global middleware, a deployed unit answered every authenticated route with a 401 against a clean build log.
|
|
60
|
+
|
|
61
|
+
The inspector now also records each `addGlobalMiddleware` file in `middleware.globalFiles`, a plain set that no key collision can corrupt; codegen emits the side-effect imports from it, generates the middleware file when global middleware is a project's only middleware, and asserts against the text it emitted that every such file is imported — naming the files if not, rather than letting the unit deploy without its auth gate.
|
|
62
|
+
|
|
63
|
+
- dfd7019: Resolve the MCP server instance per log message, and restore the wireAddon
|
|
64
|
+
declaration's source file.
|
|
65
|
+
- 1469e73: The app names which of an addon's functions reach MCP
|
|
66
|
+
|
|
67
|
+
`wireAddon`'s `mcp` takes a list as well as `true`. `true` still offers every
|
|
68
|
+
function the addon declared `mcp: true`; a list names the tools this deployment
|
|
69
|
+
offers, whether or not the addon declared them, and is typed against the
|
|
70
|
+
function names that addon publishes — a typo is a compile error rather than a
|
|
71
|
+
tool silently missing from the menu.
|
|
72
|
+
|
|
73
|
+
- Updated dependencies [c842054]
|
|
74
|
+
- Updated dependencies [dfd7019]
|
|
75
|
+
- Updated dependencies [dfd7019]
|
|
76
|
+
- Updated dependencies [9b978e7]
|
|
77
|
+
- Updated dependencies [1469e73]
|
|
78
|
+
- @pikku/core@0.12.113
|
|
79
|
+
|
|
1
80
|
## 0.12.81
|
|
2
81
|
|
|
3
82
|
### Patch Changes
|
|
@@ -11,13 +11,16 @@ const wrapperFunctionMap = {
|
|
|
11
11
|
pikkuServerLifecycle: 'ServerLifecycle',
|
|
12
12
|
};
|
|
13
13
|
/**
|
|
14
|
-
* What an addon's
|
|
15
|
-
* destructured off
|
|
16
|
-
* `const { … } = existingServices` in the body.
|
|
14
|
+
* What an addon's services factory takes from the parent: the names
|
|
15
|
+
* destructured off the parameter carrying the parent's bag, either in the
|
|
16
|
+
* parameter list or from a `const { … } = existingServices` in the body.
|
|
17
|
+
*
|
|
18
|
+
* `pikkuAddonServices` receives it second, after the config;
|
|
19
|
+
* `pikkuAddonWireServices` receives it first, ahead of the wire.
|
|
17
20
|
*/
|
|
18
|
-
const extractForwardedServices = (functionNode) => {
|
|
21
|
+
const extractForwardedServices = (functionNode, servicesParamIndex) => {
|
|
19
22
|
const forwarded = [];
|
|
20
|
-
const secondParam = functionNode.parameters[
|
|
23
|
+
const secondParam = functionNode.parameters[servicesParamIndex];
|
|
21
24
|
if (!secondParam)
|
|
22
25
|
return forwarded;
|
|
23
26
|
const collectBinding = (pattern) => {
|
|
@@ -43,6 +46,22 @@ const extractForwardedServices = (functionNode) => {
|
|
|
43
46
|
const body = functionNode.body;
|
|
44
47
|
if (!ts.isBlock(body))
|
|
45
48
|
return forwarded;
|
|
49
|
+
/**
|
|
50
|
+
* A nested callback is free to name its own parameter `services` too. Its
|
|
51
|
+
* destructuring belongs to that parameter, not the factory's, so descending
|
|
52
|
+
* into it would put a service on the addon's contract the factory never
|
|
53
|
+
* asked the parent for.
|
|
54
|
+
*/
|
|
55
|
+
const shadowsParam = (node) => {
|
|
56
|
+
if (!ts.isFunctionLike(node))
|
|
57
|
+
return false;
|
|
58
|
+
return node.parameters.some((parameter) => bindsName(parameter.name));
|
|
59
|
+
};
|
|
60
|
+
const bindsName = (name) => {
|
|
61
|
+
if (ts.isIdentifier(name))
|
|
62
|
+
return name.text === paramName;
|
|
63
|
+
return name.elements.some((element) => !ts.isOmittedExpression(element) && bindsName(element.name));
|
|
64
|
+
};
|
|
46
65
|
const visit = (node) => {
|
|
47
66
|
if (ts.isVariableDeclaration(node) &&
|
|
48
67
|
ts.isObjectBindingPattern(node.name) &&
|
|
@@ -50,7 +69,10 @@ const extractForwardedServices = (functionNode) => {
|
|
|
50
69
|
ts.isIdentifier(node.initializer) &&
|
|
51
70
|
node.initializer.text === paramName) {
|
|
52
71
|
collectBinding(node.name);
|
|
72
|
+
return;
|
|
53
73
|
}
|
|
74
|
+
if (shadowsParam(node))
|
|
75
|
+
return;
|
|
54
76
|
ts.forEachChild(node, visit);
|
|
55
77
|
};
|
|
56
78
|
ts.forEachChild(body, visit);
|
|
@@ -143,10 +165,15 @@ export const addFileWithFactory = (node, checker, methods = new Map(), expectedT
|
|
|
143
165
|
state.wireServicesMeta.set(variableName, servicesMeta.services);
|
|
144
166
|
}
|
|
145
167
|
// Extract existing services an addon needs from the parent
|
|
146
|
-
// (second parameter of pikkuAddonServices callback)
|
|
147
|
-
|
|
168
|
+
// (second parameter of pikkuAddonServices callback). A wire
|
|
169
|
+
// factory reads the same parent bag and returns services of its
|
|
170
|
+
// own, so it declares the same contract — a service it builds per
|
|
171
|
+
// wire is one the addon owns, not one its consumer owes.
|
|
172
|
+
if ((wrapperFunctionName === 'pikkuAddonServices' ||
|
|
173
|
+
wrapperFunctionName === 'pikkuAddonWireServices') &&
|
|
174
|
+
functionNode) {
|
|
148
175
|
state.addonServicesFactorySeen = true;
|
|
149
|
-
const forwarded = new Set(extractForwardedServices(functionNode));
|
|
176
|
+
const forwarded = new Set(extractForwardedServices(functionNode, wrapperFunctionName === 'pikkuAddonWireServices' ? 0 : 1));
|
|
150
177
|
for (const name of forwarded) {
|
|
151
178
|
state.addonRequiredParentServices.push(name);
|
|
152
179
|
}
|
|
@@ -47,6 +47,52 @@ function recordMiddlewareGroup(groups, key, group) {
|
|
|
47
47
|
services: Array.from(new Set([...existing.services.services, ...group.services.services])),
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Record one `add*Middleware` call's entries in the instance map, giving each
|
|
52
|
+
* entry an id that is unique across the whole inspection.
|
|
53
|
+
*
|
|
54
|
+
* The index used to restart at 0 for every call, so a second file registering
|
|
55
|
+
* against the same group (or against `global`, which is one group for the whole
|
|
56
|
+
* project) minted ids the first file already owned and silently overwrote its
|
|
57
|
+
* entries. For `global:*` that is fatal rather than cosmetic: the instance map
|
|
58
|
+
* is the only record of a global registration, and per-unit deploy codegen
|
|
59
|
+
* emits its side-effect imports from that map — a lost entry means the module
|
|
60
|
+
* is never imported, `addGlobalMiddleware` never runs, and an auth gate is
|
|
61
|
+
* simply absent from the deployed unit with nothing in the build log.
|
|
62
|
+
*
|
|
63
|
+
* Allocating from the ids already present makes N registrations against one
|
|
64
|
+
* group produce N distinct entries, whatever order the files are visited in.
|
|
65
|
+
*/
|
|
66
|
+
function registerMiddlewareInstances(instances, groupType, groupKey, entries, sourceFile, position) {
|
|
67
|
+
// The inspector may revisit a file; the same call site is not a second
|
|
68
|
+
// registration, and re-allocating would inflate the map on every pass.
|
|
69
|
+
const alreadyRecorded = Object.entries(instances)
|
|
70
|
+
.filter(([id, instance]) => instance.sourceFile === sourceFile &&
|
|
71
|
+
instance.position === position &&
|
|
72
|
+
id.startsWith(`${makeContextBasedId(groupType, groupKey)}:`))
|
|
73
|
+
.map(([id]) => id);
|
|
74
|
+
if (alreadyRecorded.length === entries.length) {
|
|
75
|
+
return alreadyRecorded;
|
|
76
|
+
}
|
|
77
|
+
const instanceIds = [];
|
|
78
|
+
let next = 0;
|
|
79
|
+
for (const entry of entries) {
|
|
80
|
+
let instanceId = makeContextBasedId(groupType, groupKey, String(next));
|
|
81
|
+
while (instances[instanceId]) {
|
|
82
|
+
next++;
|
|
83
|
+
instanceId = makeContextBasedId(groupType, groupKey, String(next));
|
|
84
|
+
}
|
|
85
|
+
instances[instanceId] = {
|
|
86
|
+
definitionId: entry.definitionId,
|
|
87
|
+
sourceFile,
|
|
88
|
+
position,
|
|
89
|
+
isFactoryCall: entry.isFactoryCall,
|
|
90
|
+
};
|
|
91
|
+
instanceIds.push(instanceId);
|
|
92
|
+
next++;
|
|
93
|
+
}
|
|
94
|
+
return instanceIds;
|
|
95
|
+
}
|
|
50
96
|
function renameTempDefinitions(state, definitionIds, groupType, groupKey, storeKey = 'middleware') {
|
|
51
97
|
const tempIndices = definitionIds
|
|
52
98
|
.map((name, i) => (name.startsWith('__temp_') ? i : -1))
|
|
@@ -237,17 +283,10 @@ export const addMiddleware = (logger, node, checker, state) => {
|
|
|
237
283
|
renameTempDefinitions(state, definitionIds, 'tag', tag);
|
|
238
284
|
}
|
|
239
285
|
const sourceFile = node.getSourceFile().fileName;
|
|
240
|
-
const instanceIds =
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
definitionId: definitionIds[i],
|
|
245
|
-
sourceFile,
|
|
246
|
-
position: node.getStart(),
|
|
247
|
-
isFactoryCall: refs[i].isFactoryCall,
|
|
248
|
-
};
|
|
249
|
-
instanceIds.push(instanceId);
|
|
250
|
-
}
|
|
286
|
+
const instanceIds = registerMiddlewareInstances(state.middleware.instances, 'tag', tag, refs.map((ref, i) => ({
|
|
287
|
+
definitionId: definitionIds[i],
|
|
288
|
+
isFactoryCall: ref.isFactoryCall,
|
|
289
|
+
})), sourceFile, node.getStart());
|
|
251
290
|
const allServices = new Set();
|
|
252
291
|
for (const defId of definitionIds) {
|
|
253
292
|
const def = state.middleware.definitions[defId];
|
|
@@ -309,15 +348,14 @@ export const addMiddleware = (logger, node, checker, state) => {
|
|
|
309
348
|
renameTempDefinitions(state, definitionIds, 'global', 'middleware');
|
|
310
349
|
}
|
|
311
350
|
const sourceFile = node.getSourceFile().fileName;
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
}
|
|
351
|
+
registerMiddlewareInstances(state.middleware.instances, 'global', 'middleware', refs.map((ref, i) => ({
|
|
352
|
+
definitionId: definitionIds[i],
|
|
353
|
+
isFactoryCall: ref.isFactoryCall,
|
|
354
|
+
})), sourceFile, node.getStart());
|
|
355
|
+
// The instance map is one map for the whole project, and codegen reads it
|
|
356
|
+
// by string-prefix. This set is the independent witness that lets codegen
|
|
357
|
+
// fail loudly if a global registration ever goes missing from it.
|
|
358
|
+
state.middleware.globalFiles.add(sourceFile);
|
|
321
359
|
// Without this, bootstrap codegen's "import every file with a wire-call"
|
|
322
360
|
// pass skips middleware-only files and the registration never runs.
|
|
323
361
|
state.http.files.add(sourceFile);
|
|
@@ -347,17 +385,10 @@ export const addMiddleware = (logger, node, checker, state) => {
|
|
|
347
385
|
renameTempDefinitions(state, definitionIds, 'http', pattern);
|
|
348
386
|
}
|
|
349
387
|
const sourceFile = node.getSourceFile().fileName;
|
|
350
|
-
const instanceIds =
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
definitionId: definitionIds[i],
|
|
355
|
-
sourceFile,
|
|
356
|
-
position: node.getStart(),
|
|
357
|
-
isFactoryCall: refs[i].isFactoryCall,
|
|
358
|
-
};
|
|
359
|
-
instanceIds.push(instanceId);
|
|
360
|
-
}
|
|
388
|
+
const instanceIds = registerMiddlewareInstances(state.middleware.instances, 'http', pattern, refs.map((ref, i) => ({
|
|
389
|
+
definitionId: definitionIds[i],
|
|
390
|
+
isFactoryCall: ref.isFactoryCall,
|
|
391
|
+
})), sourceFile, node.getStart());
|
|
361
392
|
const allServices = new Set();
|
|
362
393
|
for (const defId of definitionIds) {
|
|
363
394
|
const def = state.middleware.definitions[defId];
|
|
@@ -581,17 +612,10 @@ export const addMiddleware = (logger, node, checker, state) => {
|
|
|
581
612
|
const definitionIds = refs.map((r) => r.definitionId);
|
|
582
613
|
renameTempDefinitions(state, definitionIds, 'tag', tag, 'channelMiddleware');
|
|
583
614
|
const sourceFile = node.getSourceFile().fileName;
|
|
584
|
-
const instanceIds =
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
definitionId: definitionIds[i],
|
|
589
|
-
sourceFile,
|
|
590
|
-
position: node.getStart(),
|
|
591
|
-
isFactoryCall: refs[i].isFactoryCall,
|
|
592
|
-
};
|
|
593
|
-
instanceIds.push(instanceId);
|
|
594
|
-
}
|
|
615
|
+
const instanceIds = registerMiddlewareInstances(state.channelMiddleware.instances, 'tag', tag, refs.map((ref, i) => ({
|
|
616
|
+
definitionId: definitionIds[i],
|
|
617
|
+
isFactoryCall: ref.isFactoryCall,
|
|
618
|
+
})), sourceFile, node.getStart());
|
|
595
619
|
const allServices = new Set();
|
|
596
620
|
for (const defId of definitionIds) {
|
|
597
621
|
const def = state.channelMiddleware.definitions[defId];
|
|
@@ -30,6 +30,54 @@ function parseStringRecord(obj) {
|
|
|
30
30
|
}
|
|
31
31
|
return result;
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* `credentialOverrides` takes either a rename string or an object that also
|
|
35
|
+
* decides where the value is read from, so it cannot use `parseStringRecord`.
|
|
36
|
+
* A non-literal member is dropped rather than guessed at — the wiring is read
|
|
37
|
+
* statically, and a value this cannot see must not be reported as a mode.
|
|
38
|
+
*/
|
|
39
|
+
function parseCredentialOverrideRecord(obj) {
|
|
40
|
+
const result = {};
|
|
41
|
+
for (const prop of obj.properties) {
|
|
42
|
+
if (!ts.isPropertyAssignment(prop))
|
|
43
|
+
continue;
|
|
44
|
+
const keyNode = prop.name;
|
|
45
|
+
const key = ts.isIdentifier(keyNode)
|
|
46
|
+
? keyNode.text
|
|
47
|
+
: ts.isStringLiteral(keyNode)
|
|
48
|
+
? keyNode.text
|
|
49
|
+
: undefined;
|
|
50
|
+
if (!key)
|
|
51
|
+
continue;
|
|
52
|
+
if (ts.isStringLiteral(prop.initializer)) {
|
|
53
|
+
result[key] = prop.initializer.text;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (!ts.isObjectLiteralExpression(prop.initializer))
|
|
57
|
+
continue;
|
|
58
|
+
const override = {};
|
|
59
|
+
for (const member of prop.initializer.properties) {
|
|
60
|
+
if (!ts.isPropertyAssignment(member))
|
|
61
|
+
continue;
|
|
62
|
+
const memberKey = ts.isIdentifier(member.name)
|
|
63
|
+
? member.name.text
|
|
64
|
+
: ts.isStringLiteral(member.name)
|
|
65
|
+
? member.name.text
|
|
66
|
+
: undefined;
|
|
67
|
+
if (!memberKey || !ts.isStringLiteral(member.initializer))
|
|
68
|
+
continue;
|
|
69
|
+
if (memberKey === 'name')
|
|
70
|
+
override.name = member.initializer.text;
|
|
71
|
+
else if (memberKey === 'mode' &&
|
|
72
|
+
(member.initializer.text === 'wire' ||
|
|
73
|
+
member.initializer.text === 'singleton')) {
|
|
74
|
+
override.mode = member.initializer.text;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
result[key] = override;
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
33
81
|
/**
|
|
34
82
|
* Detect wireAddon({ name: '...', package: '...' }) call expressions and
|
|
35
83
|
* populate state.rpc.wireAddonDeclarations and state.rpc.usedAddons.
|
|
@@ -70,10 +118,12 @@ export function addWireAddon(node, state, logger) {
|
|
|
70
118
|
else if (key === 'rpcEndpoint' && ts.isStringLiteral(prop.initializer)) {
|
|
71
119
|
rpcEndpoint = prop.initializer.text;
|
|
72
120
|
}
|
|
73
|
-
else if (key === 'mcp'
|
|
74
|
-
|
|
75
|
-
prop.initializer.kind === ts.SyntaxKind.
|
|
76
|
-
|
|
121
|
+
else if (key === 'mcp') {
|
|
122
|
+
mcp =
|
|
123
|
+
prop.initializer.kind === ts.SyntaxKind.TrueKeyword ||
|
|
124
|
+
prop.initializer.kind === ts.SyntaxKind.FalseKeyword
|
|
125
|
+
? prop.initializer.kind === ts.SyntaxKind.TrueKeyword
|
|
126
|
+
: parseStringArray(prop.initializer);
|
|
77
127
|
}
|
|
78
128
|
else if (key === 'auth' &&
|
|
79
129
|
(prop.initializer.kind === ts.SyntaxKind.TrueKeyword ||
|
|
@@ -96,7 +146,7 @@ export function addWireAddon(node, state, logger) {
|
|
|
96
146
|
}
|
|
97
147
|
else if (key === 'credentialOverrides' &&
|
|
98
148
|
ts.isObjectLiteralExpression(prop.initializer)) {
|
|
99
|
-
credentialOverrides =
|
|
149
|
+
credentialOverrides = parseCredentialOverrideRecord(prop.initializer);
|
|
100
150
|
}
|
|
101
151
|
else if (key === 'secretGrants') {
|
|
102
152
|
secretGrants = parseStringArray(prop.initializer);
|
package/dist/error-codes.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ export declare enum ErrorCode {
|
|
|
32
32
|
REMOTE_ADDON_NOT_DEV_DEPENDENCY = "PKU338",
|
|
33
33
|
REMOTE_ADDON_AUTH_UNRESOLVED = "PKU339",
|
|
34
34
|
ADDON_NOT_INSTALLED = "PKU340",
|
|
35
|
+
ADDON_MCP_FUNCTION_NOT_FOUND = "PKU341",
|
|
35
36
|
DB_COLUMN_NAME_TYPE_CONTRADICTION = "PKU480",
|
|
36
37
|
DB_JSON_COLUMN_UNTYPED = "PKU481",
|
|
37
38
|
DB_FORMAT_ON_NON_STRING = "PKU482",
|
package/dist/error-codes.js
CHANGED
|
@@ -35,6 +35,7 @@ export var ErrorCode;
|
|
|
35
35
|
ErrorCode["REMOTE_ADDON_NOT_DEV_DEPENDENCY"] = "PKU338";
|
|
36
36
|
ErrorCode["REMOTE_ADDON_AUTH_UNRESOLVED"] = "PKU339";
|
|
37
37
|
ErrorCode["ADDON_NOT_INSTALLED"] = "PKU340";
|
|
38
|
+
ErrorCode["ADDON_MCP_FUNCTION_NOT_FOUND"] = "PKU341";
|
|
38
39
|
// Database schema codegen warnings
|
|
39
40
|
ErrorCode["DB_COLUMN_NAME_TYPE_CONTRADICTION"] = "PKU480";
|
|
40
41
|
ErrorCode["DB_JSON_COLUMN_UNTYPED"] = "PKU481";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { inspect, getInitialInspectorState } from './inspector.js';
|
|
2
2
|
export type { TypesMap } from './types-map.js';
|
|
3
|
-
export type { AddonConfig, AuthDefinition, InspectorDiagnostic, InspectorFilters, InspectorHTTPState, InspectorLogger, InspectorMiddlewareState, InspectorOptions, InspectorPermissionState, InspectorState, MetaInputTypes, MiddlewareGroupMeta, MiddlewareRegistration, SchemaRef, } from './types.js';
|
|
3
|
+
export type { AddonConfig, AuthDefinition, CredentialOverrideMeta, InspectorDiagnostic, InspectorFilters, InspectorHTTPState, InspectorLogger, InspectorMiddlewareState, InspectorOptions, InspectorPermissionState, InspectorState, MetaInputTypes, MiddlewareGroupMeta, MiddlewareRegistration, SchemaRef, } from './types.js';
|
|
4
4
|
export type { FilesAndMethodsErrors } from './utils/get-files-and-methods.js';
|
|
5
5
|
export { ErrorCode } from './error-codes.js';
|
|
6
6
|
export type { DiagnosticSeverity, CodedDiagnostic } from './error-codes.js';
|
package/dist/inspector.js
CHANGED
|
@@ -7,7 +7,7 @@ import { getFilesAndMethods } from './utils/get-files-and-methods.js';
|
|
|
7
7
|
import { findCommonAncestor } from './utils/find-root-dir.js';
|
|
8
8
|
import { createSourceFileCacheHost } from './source-file-cache.js';
|
|
9
9
|
import { createNestedProjectFilter } from './utils/nested-project-filter.js';
|
|
10
|
-
import { aggregateRequiredServices, stampAuthHandlerServices, validateAgentModels, validateAgentToolReferences, validateAgentScorerReferences, validateSecretOverrides, validateVariableOverrides, validateCredentialOverrides, validateRemoteAddonDependencies, validateRemoteAddonAuth, validateScopeReferences, validateSystemRoleScopes, validatePersonaRoles, computeResolvedIOTypes, computeMiddlewareGroupsMeta, computePermissionsGroupsMeta, computeRequiredSchemas, validateNoSecretAliasServices, validateSecretUsage, computeDiagnostics, validateSchemaReferences, validateSchemaWiringSeparation, validateScenarioServices, validateScenarioSteps, validateWorkflowGraphAddons, } from './utils/post-process.js';
|
|
10
|
+
import { aggregateRequiredServices, stampAuthHandlerServices, validateAgentModels, validateAgentToolReferences, validateAgentScorerReferences, validateSecretOverrides, validateVariableOverrides, validateCredentialOverrides, validateRemoteAddonDependencies, validateRemoteAddonAuth, validateScopeReferences, validateSystemRoleScopes, validatePersonaRoles, computeResolvedIOTypes, computeMiddlewareGroupsMeta, computePermissionsGroupsMeta, computeRequiredSchemas, validateNoSecretAliasServices, registerDerivedOAuth2AppSecrets, validateSecretUsage, computeDiagnostics, validateSchemaReferences, validateSchemaWiringSeparation, validateScenarioServices, validateScenarioSteps, validateWorkflowGraphAddons, } from './utils/post-process.js';
|
|
11
11
|
import { validateExposedFunctionsGated } from './utils/validate-exposed-functions-gated.js';
|
|
12
12
|
import { validateTagsResolveToMiddleware } from './utils/validate-tags-resolve-to-middleware.js';
|
|
13
13
|
import { annotateHttpRouteAuth } from './utils/annotate-http-route-auth.js';
|
|
@@ -181,6 +181,7 @@ export function getInitialInspectorState(rootDir) {
|
|
|
181
181
|
definitions: {},
|
|
182
182
|
instances: {},
|
|
183
183
|
tagMiddleware: new Map(),
|
|
184
|
+
globalFiles: new Set(),
|
|
184
185
|
},
|
|
185
186
|
channelMiddleware: {
|
|
186
187
|
definitions: {},
|
|
@@ -396,6 +397,7 @@ export const inspect = async (logger, routeFiles, options = {}) => {
|
|
|
396
397
|
computeMiddlewareGroupsMeta(state);
|
|
397
398
|
computePermissionsGroupsMeta(state);
|
|
398
399
|
validateNoSecretAliasServices(logger, checker, state);
|
|
400
|
+
registerDerivedOAuth2AppSecrets(state);
|
|
399
401
|
validateSecretUsage(logger, state);
|
|
400
402
|
computeDiagnostics(state);
|
|
401
403
|
validateSchemaWiringSeparation(logger, state);
|
package/dist/types.d.ts
CHANGED
|
@@ -162,6 +162,16 @@ export interface InspectorMiddlewareState {
|
|
|
162
162
|
definitions: Record<string, InspectorMiddlewareDefinition>;
|
|
163
163
|
instances: Record<string, InspectorMiddlewareInstance>;
|
|
164
164
|
tagMiddleware: Map<string, MiddlewareGroupMeta>;
|
|
165
|
+
/**
|
|
166
|
+
* Every source file with a top-level `addGlobalMiddleware` call.
|
|
167
|
+
*
|
|
168
|
+
* Global middleware belongs to no wire group, so the only thing that makes it
|
|
169
|
+
* run in a generated bundle is a side-effect import of its source file. This
|
|
170
|
+
* set is the record codegen emits those imports from, and the one it checks
|
|
171
|
+
* itself against — a plain set of file names, so nothing can overwrite an
|
|
172
|
+
* entry the way a keyed map could.
|
|
173
|
+
*/
|
|
174
|
+
globalFiles: Set<string>;
|
|
165
175
|
}
|
|
166
176
|
export interface InspectorChannelMiddlewareState {
|
|
167
177
|
definitions: Record<string, InspectorMiddlewareDefinition>;
|
|
@@ -459,6 +469,14 @@ export interface InspectorFeature {
|
|
|
459
469
|
hasBefore: boolean;
|
|
460
470
|
hasAfter: boolean;
|
|
461
471
|
}
|
|
472
|
+
/**
|
|
473
|
+
* A `wireAddon` credential override as it survives static reading: a rename
|
|
474
|
+
* string, or the object form that also decides how the value resolves.
|
|
475
|
+
*/
|
|
476
|
+
export type CredentialOverrideMeta = string | {
|
|
477
|
+
name?: string;
|
|
478
|
+
mode?: 'singleton' | 'wire';
|
|
479
|
+
};
|
|
462
480
|
export interface InspectorState {
|
|
463
481
|
rootDir: string;
|
|
464
482
|
singletonServicesTypeImportMap: PathToNameAndType;
|
|
@@ -565,7 +583,12 @@ export interface InspectorState {
|
|
|
565
583
|
/** The app source file whose `wireAddon` call declared this instance. */
|
|
566
584
|
file?: string;
|
|
567
585
|
rpcEndpoint?: string;
|
|
568
|
-
|
|
586
|
+
/**
|
|
587
|
+
* `true` offers every function the addon declared `mcp: true`; a list
|
|
588
|
+
* names the functions to offer, whatever the addon declared. Absent
|
|
589
|
+
* when the declared value was not a statically-knowable literal.
|
|
590
|
+
*/
|
|
591
|
+
mcp?: boolean | string[];
|
|
569
592
|
/**
|
|
570
593
|
* The addon's own gates. `runPikkuFunc` applies these to every function
|
|
571
594
|
* in the package, on every wiring path — not just `namespace:function`.
|
|
@@ -585,7 +608,7 @@ export interface InspectorState {
|
|
|
585
608
|
authSecretId?: string;
|
|
586
609
|
secretOverrides?: Record<string, string>;
|
|
587
610
|
variableOverrides?: Record<string, string>;
|
|
588
|
-
credentialOverrides?: Record<string,
|
|
611
|
+
credentialOverrides?: Record<string, CredentialOverrideMeta>;
|
|
589
612
|
/** Secrets the app lends this instance, named as the addon reads them. */
|
|
590
613
|
secretGrants?: string[];
|
|
591
614
|
/** Credentials the app lends this instance, named as the addon reads them. */
|
|
@@ -2,6 +2,7 @@ import { existsSync } from 'fs';
|
|
|
2
2
|
import { readFile, readdir } from 'fs/promises';
|
|
3
3
|
import { createRequire } from 'module';
|
|
4
4
|
import { join, dirname } from 'path';
|
|
5
|
+
import { ErrorCode } from '../error-codes.js';
|
|
5
6
|
const isHTTPRouteConfig = (value) => typeof value === 'object' &&
|
|
6
7
|
value !== null &&
|
|
7
8
|
'method' in value &&
|
|
@@ -147,19 +148,31 @@ export async function loadAddonFunctionsMeta(logger, state) {
|
|
|
147
148
|
state.addonFunctions[namespace] = meta;
|
|
148
149
|
logger.debug(`Loaded ${Object.keys(meta).length} addon functions for '${namespace}' from ${decl.package}`);
|
|
149
150
|
registerAddonTypes(state, metaPath, meta);
|
|
150
|
-
//
|
|
151
|
+
// `mcp: true` offers what the addon declared as tools; `mcp: [...]` names
|
|
152
|
+
// the functions to offer, whatever the addon declared — the consuming app
|
|
153
|
+
// decides which of the addon's functions a model gets to see.
|
|
151
154
|
if (decl.mcp) {
|
|
155
|
+
const selected = Array.isArray(decl.mcp) ? new Set(decl.mcp) : null;
|
|
152
156
|
for (const [funcName, funcMeta] of Object.entries(meta)) {
|
|
153
|
-
if (funcMeta.mcp) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
157
|
+
if (selected ? !selected.has(funcName) : !funcMeta.mcp) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const toolName = `${namespace}:${funcName}`;
|
|
161
|
+
state.mcpEndpoints.toolsMeta[toolName] = {
|
|
162
|
+
pikkuFuncId: `${namespace}:${funcName}`,
|
|
163
|
+
name: toolName,
|
|
164
|
+
description: funcMeta.description || funcMeta.title || funcName,
|
|
165
|
+
inputSchema: funcMeta.inputSchemaName ?? null,
|
|
166
|
+
outputSchema: funcMeta.outputSchemaName ?? null,
|
|
167
|
+
tags: funcMeta.tags,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
// A name the addon does not publish is a tool the app believes it
|
|
171
|
+
// offers and never does, so it fails the build rather than silently
|
|
172
|
+
// offering one tool fewer than the list says.
|
|
173
|
+
for (const funcName of selected ?? []) {
|
|
174
|
+
if (!(funcName in meta)) {
|
|
175
|
+
logger.critical(ErrorCode.ADDON_MCP_FUNCTION_NOT_FOUND, `wireAddon('${namespace}') lists '${funcName}' under mcp, but ${decl.package} publishes no such function.`);
|
|
163
176
|
}
|
|
164
177
|
}
|
|
165
178
|
}
|
|
@@ -161,6 +161,12 @@ export declare function validateNoSecretAliasServices(logger: InspectorLogger, c
|
|
|
161
161
|
* at first request rather than at deploy; a non-literal key is a read the
|
|
162
162
|
* manifest cannot cover, so a per-unit scope cannot be narrowed around it.
|
|
163
163
|
*/
|
|
164
|
+
/**
|
|
165
|
+
* Registers the app secrets the project's OAuth2 credentials imply, so a
|
|
166
|
+
* deployment is asked for the client id and secret every connect flow needs
|
|
167
|
+
* without an author restating a shape the runtime already fixes.
|
|
168
|
+
*/
|
|
169
|
+
export declare function registerDerivedOAuth2AppSecrets(state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
|
|
164
170
|
export declare function validateSecretUsage(logger: InspectorLogger, state: InspectorState | Omit<InspectorState, 'typesLookup'>): void;
|
|
165
171
|
export declare function computeDiagnostics(state: InspectorState): void;
|
|
166
172
|
/**
|
|
@@ -4,6 +4,7 @@ import { join } from 'node:path';
|
|
|
4
4
|
import { extractTypeKeys } from './type-utils.js';
|
|
5
5
|
import { ErrorCode } from '../error-codes.js';
|
|
6
6
|
import { findSecretAliasServices } from './secret-alias-services.js';
|
|
7
|
+
import { deriveOAuth2AppSecrets } from '@pikku/core/secret';
|
|
7
8
|
import { resolveCoreType } from './resolve-core-type.js';
|
|
8
9
|
import { relative } from 'node:path';
|
|
9
10
|
import { AUTH_HANDLER_FUNC_ID } from '../add/add-auth.js';
|
|
@@ -385,15 +386,42 @@ export function validateCredentialOverrides(logger, state) {
|
|
|
385
386
|
if (!wireAddonDeclarations || wireAddonDeclarations.size === 0)
|
|
386
387
|
return;
|
|
387
388
|
const credentialNames = new Set(state.credentials?.definitions.map((d) => d.name) ?? []);
|
|
389
|
+
/** Only a rename has a target to check; a mode-only override renames nothing. */
|
|
390
|
+
const renameTarget = (override) => typeof override === 'string' ? override : override.name;
|
|
391
|
+
const overrideMode = (override) => typeof override === 'string' ? undefined : override.mode;
|
|
392
|
+
/**
|
|
393
|
+
* Which declaration set each resolved name's mode. Credential generation
|
|
394
|
+
* writes one shared metadata entry per name, so two declarations that map
|
|
395
|
+
* onto one name with opposite modes leave whichever ran last in the file the
|
|
396
|
+
* console reads — and the other addon silently gets the wrong resolution.
|
|
397
|
+
*/
|
|
398
|
+
const modeClaims = new Map();
|
|
388
399
|
for (const [namespace, addonDecl] of wireAddonDeclarations.entries()) {
|
|
389
|
-
for (const [logicalName,
|
|
400
|
+
for (const [logicalName, override] of Object.entries(addonDecl.credentialOverrides ?? {})) {
|
|
401
|
+
// A mode-only override still names a credential — the addon's own. It
|
|
402
|
+
// has to exist, or generation resolves nothing and the credential keeps
|
|
403
|
+
// its default mode with no sign that the wiring asked for another.
|
|
404
|
+
const renamed = renameTarget(override);
|
|
405
|
+
const resolvedName = renamed ?? logicalName;
|
|
390
406
|
if (!credentialNames.has(resolvedName)) {
|
|
391
407
|
const availableCredentials = Array.from(credentialNames);
|
|
392
|
-
|
|
408
|
+
const target = renamed
|
|
409
|
+
? `'${logicalName}' -> '${resolvedName}'`
|
|
410
|
+
: `'${logicalName}'`;
|
|
411
|
+
logger.critical(ErrorCode.INVALID_VALUE, `Credential override ${target} in addon '${namespace}' (${addonDecl.package}) targets a credential that does not exist. Available credentials: ${availableCredentials.join(', ') || 'none'}`);
|
|
393
412
|
}
|
|
413
|
+
const mode = overrideMode(override);
|
|
414
|
+
if (!mode)
|
|
415
|
+
continue;
|
|
416
|
+
const claim = modeClaims.get(resolvedName);
|
|
417
|
+
if (claim && claim.mode !== mode) {
|
|
418
|
+
logger.critical(ErrorCode.INVALID_VALUE, `Credential '${resolvedName}' is wired '${claim.mode}' by '${claim.logicalName}' in addon '${claim.namespace}' and '${mode}' by '${logicalName}' in addon '${namespace}'. One credential holds one mode, so the second wiring would silently take the first's resolution. Give them separate names, or wire both the same way.`);
|
|
419
|
+
}
|
|
420
|
+
modeClaims.set(resolvedName, { mode, namespace, logicalName });
|
|
394
421
|
}
|
|
395
422
|
for (const logicalName of addonDecl.credentialGrants ?? []) {
|
|
396
|
-
const
|
|
423
|
+
const override = addonDecl.credentialOverrides?.[logicalName];
|
|
424
|
+
const resolvedName = (override ? renameTarget(override) : undefined) ?? logicalName;
|
|
397
425
|
if (!credentialNames.has(resolvedName)) {
|
|
398
426
|
const availableCredentials = Array.from(credentialNames);
|
|
399
427
|
logger.critical(ErrorCode.INVALID_VALUE, `Credential grant '${logicalName}' in addon '${namespace}' (${addonDecl.package}) targets a credential that does not exist. Available credentials: ${availableCredentials.join(', ') || 'none'}`);
|
|
@@ -1029,6 +1057,15 @@ export function validateNoSecretAliasServices(logger, checker, state) {
|
|
|
1029
1057
|
* at first request rather than at deploy; a non-literal key is a read the
|
|
1030
1058
|
* manifest cannot cover, so a per-unit scope cannot be narrowed around it.
|
|
1031
1059
|
*/
|
|
1060
|
+
/**
|
|
1061
|
+
* Registers the app secrets the project's OAuth2 credentials imply, so a
|
|
1062
|
+
* deployment is asked for the client id and secret every connect flow needs
|
|
1063
|
+
* without an author restating a shape the runtime already fixes.
|
|
1064
|
+
*/
|
|
1065
|
+
export function registerDerivedOAuth2AppSecrets(state) {
|
|
1066
|
+
const derived = deriveOAuth2AppSecrets(state.credentials?.definitions ?? [], state.secrets.definitions);
|
|
1067
|
+
state.secrets.definitions.push(...derived);
|
|
1068
|
+
}
|
|
1032
1069
|
export function validateSecretUsage(logger, state) {
|
|
1033
1070
|
const declared = new Set(state.secrets.definitions.map((d) => d.secretId));
|
|
1034
1071
|
for (const definition of state.secrets.definitions) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CredentialOverrideMeta } from '../types.js';
|
|
1
2
|
import * as ts from 'typescript';
|
|
2
3
|
/**
|
|
3
4
|
* Resolve the addon package name from an imported identifier.
|
|
@@ -12,5 +13,5 @@ export declare const resolveAddonName: (identifier: ts.Identifier, checker: ts.T
|
|
|
12
13
|
rpcEndpoint?: string;
|
|
13
14
|
secretOverrides?: Record<string, string>;
|
|
14
15
|
variableOverrides?: Record<string, string>;
|
|
15
|
-
credentialOverrides?: Record<string,
|
|
16
|
+
credentialOverrides?: Record<string, CredentialOverrideMeta>;
|
|
16
17
|
}>) => string | null;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CredentialOverrideMeta } from '../types.js';
|
|
1
2
|
import type { SecretUsage } from './extract-secret-usage.js';
|
|
2
3
|
import type { JSONValue } from '@pikku/core/utils';
|
|
3
4
|
import type { InspectorDiagnostic, InspectorFeature, InspectorState } from '../types.js';
|
|
@@ -197,7 +198,7 @@ export interface SerializableInspectorState {
|
|
|
197
198
|
authSecretId?: string;
|
|
198
199
|
secretOverrides?: Record<string, string>;
|
|
199
200
|
variableOverrides?: Record<string, string>;
|
|
200
|
-
credentialOverrides?: Record<string,
|
|
201
|
+
credentialOverrides?: Record<string, CredentialOverrideMeta>;
|
|
201
202
|
secretGrants?: string[];
|
|
202
203
|
credentialGrants?: string[];
|
|
203
204
|
globalSecrets?: string;
|
|
@@ -275,6 +276,7 @@ export interface SerializableInspectorState {
|
|
|
275
276
|
middleware: {
|
|
276
277
|
definitions: InspectorState['middleware']['definitions'];
|
|
277
278
|
instances: InspectorState['middleware']['instances'];
|
|
279
|
+
globalFiles: string[];
|
|
278
280
|
tagMiddleware: Array<[
|
|
279
281
|
string,
|
|
280
282
|
InspectorState['middleware']['tagMiddleware'] extends Map<string, infer V> ? V : never
|
|
@@ -150,6 +150,7 @@ export function serializeInspectorState(state) {
|
|
|
150
150
|
middleware: {
|
|
151
151
|
definitions: state.middleware.definitions,
|
|
152
152
|
instances: state.middleware.instances,
|
|
153
|
+
globalFiles: Array.from(state.middleware.globalFiles),
|
|
153
154
|
tagMiddleware: Array.from(state.middleware.tagMiddleware.entries()),
|
|
154
155
|
},
|
|
155
156
|
channelMiddleware: {
|
|
@@ -335,6 +336,7 @@ export function deserializeInspectorState(data) {
|
|
|
335
336
|
middleware: {
|
|
336
337
|
definitions: data.middleware.definitions,
|
|
337
338
|
instances: data.middleware.instances || {},
|
|
339
|
+
globalFiles: new Set(data.middleware.globalFiles || []),
|
|
338
340
|
tagMiddleware: new Map(data.middleware.tagMiddleware),
|
|
339
341
|
},
|
|
340
342
|
channelMiddleware: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/inspector",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.83",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/pikkujs/pikku.git",
|
|
@@ -55,10 +55,10 @@
|
|
|
55
55
|
"zod-to-ts": "^2.1.0"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
|
-
"@pikku/core": "^0.12.
|
|
58
|
+
"@pikku/core": "^0.12.114"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@pikku/core": "^0.12.
|
|
61
|
+
"@pikku/core": "^0.12.114",
|
|
62
62
|
"@types/node": "^24.13.3"
|
|
63
63
|
},
|
|
64
64
|
"engines": {
|