@pikku/inspector 0.12.91 → 0.12.92
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 +9 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/utils/addon-resolution.d.ts +10 -0
- package/dist/utils/addon-resolution.js +43 -0
- package/dist/utils/load-addon-functions-meta.js +1 -40
- package/dist/utils/post-process.js +49 -36
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## 0.12.92
|
|
2
|
+
|
|
3
|
+
### Patch Changes
|
|
4
|
+
|
|
5
|
+
- f84e173: An addon wired from a workspace package now resolves from that package. The installed-addon check (PKU340), the remote addon `devDependencies` check and `pikku db generate`'s addon schemas all looked only at the project root, so an addon declared where it is wired — the only place bun links it — failed codegen unless it was also declared at the root.
|
|
6
|
+
- Updated dependencies [f02585e]
|
|
7
|
+
- Updated dependencies [5586749]
|
|
8
|
+
- @pikku/core@0.12.127
|
|
9
|
+
|
|
1
10
|
## 0.12.91
|
|
2
11
|
|
|
3
12
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -26,3 +26,4 @@ export type { SerializedWorkflowGraph, SerializedWorkflowGraphs, } from './utils
|
|
|
26
26
|
export { resolveCoreType } from './utils/resolve-core-type.js';
|
|
27
27
|
export { readModuleSpecifiers } from './utils/read-module-specifiers.js';
|
|
28
28
|
export { readTsconfigOutDir } from './utils/read-tsconfig-out-dir.js';
|
|
29
|
+
export { addonResolutionDirs, createAddonResolver, } from './utils/addon-resolution.js';
|
package/dist/index.js
CHANGED
|
@@ -15,3 +15,4 @@ export { resolveFunctionMeta } from './utils/resolve-function-meta.js';
|
|
|
15
15
|
export { resolveCoreType } from './utils/resolve-core-type.js';
|
|
16
16
|
export { readModuleSpecifiers } from './utils/read-module-specifiers.js';
|
|
17
17
|
export { readTsconfigOutDir } from './utils/read-tsconfig-out-dir.js';
|
|
18
|
+
export { addonResolutionDirs, createAddonResolver, } from './utils/addon-resolution.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type AddonResolver = {
|
|
2
|
+
resolve: (id: string) => string;
|
|
3
|
+
};
|
|
4
|
+
/**
|
|
5
|
+
* An addon is installed wherever the package that calls `wireAddon` lists it,
|
|
6
|
+
* which in a workspace is usually not the repo root. Resolve from the declaring
|
|
7
|
+
* package first, as its code does at runtime, then from the root.
|
|
8
|
+
*/
|
|
9
|
+
export declare const addonResolutionDirs: (rootDir: string, declFile?: string) => string[];
|
|
10
|
+
export declare const createAddonResolver: (dirs: string[]) => AddonResolver;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { createRequire } from 'module';
|
|
3
|
+
import { join, dirname, parse, resolve } from 'path';
|
|
4
|
+
const nearestPackageDir = (file) => {
|
|
5
|
+
const root = parse(file).root;
|
|
6
|
+
let dir = dirname(file);
|
|
7
|
+
while (dir && dir !== root) {
|
|
8
|
+
if (existsSync(join(dir, 'package.json')))
|
|
9
|
+
return dir;
|
|
10
|
+
const parent = dirname(dir);
|
|
11
|
+
if (parent === dir)
|
|
12
|
+
break;
|
|
13
|
+
dir = parent;
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* An addon is installed wherever the package that calls `wireAddon` lists it,
|
|
19
|
+
* which in a workspace is usually not the repo root. Resolve from the declaring
|
|
20
|
+
* package first, as its code does at runtime, then from the root.
|
|
21
|
+
*/
|
|
22
|
+
export const addonResolutionDirs = (rootDir, declFile) => {
|
|
23
|
+
const root = resolve(rootDir);
|
|
24
|
+
const declDir = declFile ? nearestPackageDir(resolve(declFile)) : null;
|
|
25
|
+
return declDir && declDir !== root ? [declDir, root] : [root];
|
|
26
|
+
};
|
|
27
|
+
export const createAddonResolver = (dirs) => {
|
|
28
|
+
const requires = dirs.map((dir) => createRequire(join(dir, 'package.json')));
|
|
29
|
+
return {
|
|
30
|
+
resolve: (id) => {
|
|
31
|
+
let lastError;
|
|
32
|
+
for (const req of requires) {
|
|
33
|
+
try {
|
|
34
|
+
return req.resolve(id);
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
lastError = e;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
throw lastError;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'fs';
|
|
2
2
|
import { readFile, readdir } from 'fs/promises';
|
|
3
|
-
import { createRequire } from 'module';
|
|
4
3
|
import { join, dirname, parse, relative } from 'path';
|
|
4
|
+
import { addonResolutionDirs, createAddonResolver, } from './addon-resolution.js';
|
|
5
5
|
import { ErrorCode } from '../error-codes.js';
|
|
6
6
|
const isHTTPRouteConfig = (value) => typeof value === 'object' &&
|
|
7
7
|
value !== null &&
|
|
@@ -90,45 +90,6 @@ const resolveAddonMeta = (require, packageName, baseName) => {
|
|
|
90
90
|
}
|
|
91
91
|
return null;
|
|
92
92
|
};
|
|
93
|
-
const nearestPackageDir = (file) => {
|
|
94
|
-
const root = parse(file).root;
|
|
95
|
-
let dir = dirname(file);
|
|
96
|
-
while (dir && dir !== root) {
|
|
97
|
-
if (existsSync(join(dir, 'package.json')))
|
|
98
|
-
return dir;
|
|
99
|
-
const parent = dirname(dir);
|
|
100
|
-
if (parent === dir)
|
|
101
|
-
break;
|
|
102
|
-
dir = parent;
|
|
103
|
-
}
|
|
104
|
-
return null;
|
|
105
|
-
};
|
|
106
|
-
/**
|
|
107
|
-
* An addon is installed wherever the package that calls `wireAddon` lists it,
|
|
108
|
-
* which in a workspace is usually not the repo root. Resolve from the declaring
|
|
109
|
-
* package first, as its code does at runtime, then from the root.
|
|
110
|
-
*/
|
|
111
|
-
const addonResolutionDirs = (rootDir, declFile) => {
|
|
112
|
-
const declDir = declFile ? nearestPackageDir(declFile) : null;
|
|
113
|
-
return declDir && declDir !== rootDir ? [declDir, rootDir] : [rootDir];
|
|
114
|
-
};
|
|
115
|
-
const createAddonResolver = (dirs) => {
|
|
116
|
-
const requires = dirs.map((dir) => createRequire(join(dir, 'package.json')));
|
|
117
|
-
return {
|
|
118
|
-
resolve: (id) => {
|
|
119
|
-
let lastError;
|
|
120
|
-
for (const req of requires) {
|
|
121
|
-
try {
|
|
122
|
-
return req.resolve(id);
|
|
123
|
-
}
|
|
124
|
-
catch (e) {
|
|
125
|
-
lastError = e;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
throw lastError;
|
|
129
|
-
},
|
|
130
|
-
};
|
|
131
|
-
};
|
|
132
93
|
const findInstalledPackageDir = (dirs, packageName) => {
|
|
133
94
|
for (const start of dirs) {
|
|
134
95
|
const root = parse(start).root;
|
|
@@ -10,6 +10,7 @@ import { relative } from 'node:path';
|
|
|
10
10
|
import { AUTH_HANDLER_FUNC_ID } from '../add/add-auth.js';
|
|
11
11
|
import { flattenScopeDefinitions } from '@pikku/core/scope';
|
|
12
12
|
import { DYNAMIC_SCENARIO_STEP_TARGET } from './workflow/dsl/patterns.js';
|
|
13
|
+
import { addonResolutionDirs } from './addon-resolution.js';
|
|
13
14
|
/**
|
|
14
15
|
* Stamp the inspected authorize/callbacks service set onto the generated auth
|
|
15
16
|
* handler's function meta.
|
|
@@ -460,32 +461,34 @@ export function validateVariableOverrides(logger, state) {
|
|
|
460
461
|
}
|
|
461
462
|
}
|
|
462
463
|
/**
|
|
463
|
-
* Can `pkg` be resolved from `
|
|
464
|
+
* Can `pkg` be resolved from any of `dirs`, by any means the loader would use?
|
|
464
465
|
*
|
|
465
466
|
* Resolution is attempted against `<pkg>/package.json` first and the bare
|
|
466
467
|
* specifier second, because neither alone is conclusive. A package with a
|
|
467
468
|
* restrictive `exports` map refuses the first with
|
|
468
469
|
* `ERR_PACKAGE_PATH_NOT_EXPORTED` — which is not a failure at all: reaching the
|
|
469
|
-
* exports map means the package was found. Only a module-not-found from
|
|
470
|
-
*
|
|
470
|
+
* exports map means the package was found. Only a module-not-found from every
|
|
471
|
+
* attempt means it is genuinely absent.
|
|
471
472
|
*/
|
|
472
|
-
const isPackageResolvable = (pkg,
|
|
473
|
+
const isPackageResolvable = (pkg, dirs) => {
|
|
473
474
|
const NOT_FOUND = new Set(['MODULE_NOT_FOUND', 'ERR_MODULE_NOT_FOUND']);
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
req = createRequire(join(rootDir, 'package.json'));
|
|
477
|
-
}
|
|
478
|
-
catch {
|
|
479
|
-
return true; // cannot ask; do not accuse
|
|
480
|
-
}
|
|
481
|
-
for (const specifier of [`${pkg}/package.json`, pkg]) {
|
|
475
|
+
for (const dir of dirs) {
|
|
476
|
+
let req;
|
|
482
477
|
try {
|
|
483
|
-
req.
|
|
484
|
-
|
|
478
|
+
req = createRequire(join(dir, 'package.json'));
|
|
479
|
+
}
|
|
480
|
+
catch {
|
|
481
|
+
return true; // cannot ask; do not accuse
|
|
485
482
|
}
|
|
486
|
-
|
|
487
|
-
|
|
483
|
+
for (const specifier of [`${pkg}/package.json`, pkg]) {
|
|
484
|
+
try {
|
|
485
|
+
req.resolve(specifier);
|
|
488
486
|
return true;
|
|
487
|
+
}
|
|
488
|
+
catch (e) {
|
|
489
|
+
if (!NOT_FOUND.has(e?.code))
|
|
490
|
+
return true;
|
|
491
|
+
}
|
|
489
492
|
}
|
|
490
493
|
}
|
|
491
494
|
return false;
|
|
@@ -523,35 +526,45 @@ export function validateRemoteAddonDependencies(logger, state) {
|
|
|
523
526
|
for (const [namespace, decl] of wireAddonDeclarations.entries()) {
|
|
524
527
|
if (decl.remote)
|
|
525
528
|
continue;
|
|
526
|
-
|
|
529
|
+
const dirs = addonResolutionDirs(state.rootDir, decl.file);
|
|
530
|
+
if (isPackageResolvable(decl.package, dirs))
|
|
527
531
|
continue;
|
|
528
|
-
logger.critical(ErrorCode.ADDON_NOT_INSTALLED, `Addon '${namespace}' ('${decl.package}') is wired with wireAddon but cannot be resolved — every ref('${namespace}:…') will resolve to nothing and the surface will be dead at runtime.
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
532
|
+
logger.critical(ErrorCode.ADDON_NOT_INSTALLED, `Addon '${namespace}' ('${decl.package}') is wired with wireAddon but cannot be resolved from ${dirs.join(' or ')} — every ref('${namespace}:…') will resolve to nothing and the surface will be dead at runtime. Add it to the dependencies of ${join(dirs[0], 'package.json')} and install, or remove the wireAddon call.`);
|
|
533
|
+
}
|
|
534
|
+
// A remote addon belongs in the devDependencies of the package that wires
|
|
535
|
+
// it, which in a workspace is not the root.
|
|
536
|
+
const manifests = new Map();
|
|
537
|
+
const readManifest = (path) => {
|
|
538
|
+
if (manifests.has(path))
|
|
539
|
+
return manifests.get(path);
|
|
540
|
+
let manifest = null;
|
|
541
|
+
if (existsSync(path)) {
|
|
542
|
+
try {
|
|
543
|
+
manifest = JSON.parse(readFileSync(path, 'utf-8'));
|
|
544
|
+
}
|
|
545
|
+
catch (e) {
|
|
546
|
+
logger.warn(`Could not read ${path} to verify remote addon dependencies: ${e?.message ?? e}`);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
manifests.set(path, manifest);
|
|
550
|
+
return manifest;
|
|
551
|
+
};
|
|
545
552
|
for (const [namespace, decl] of wireAddonDeclarations.entries()) {
|
|
546
553
|
if (!decl.remote)
|
|
547
554
|
continue;
|
|
555
|
+
const pkgJsonPath = join(addonResolutionDirs(state.rootDir, decl.file)[0], 'package.json');
|
|
556
|
+
const pkgJson = readManifest(pkgJsonPath);
|
|
557
|
+
if (!pkgJson)
|
|
558
|
+
continue; // no manifest to check (e.g. some tests)
|
|
559
|
+
const prodDeps = pkgJson.dependencies ?? {};
|
|
560
|
+
const devDeps = pkgJson.devDependencies ?? {};
|
|
548
561
|
if (decl.package in devDeps)
|
|
549
562
|
continue; // correct
|
|
550
563
|
if (decl.package in prodDeps) {
|
|
551
|
-
logger.critical(ErrorCode.REMOTE_ADDON_NOT_DEV_DEPENDENCY, `Remote addon '${namespace}' ('${decl.package}') is a production dependency, but wireRemoteAddon consumes it for types only — its handlers run on the host. Move '${decl.package}' from "dependencies" to "devDependencies".`);
|
|
564
|
+
logger.critical(ErrorCode.REMOTE_ADDON_NOT_DEV_DEPENDENCY, `Remote addon '${namespace}' ('${decl.package}') is a production dependency in ${pkgJsonPath}, but wireRemoteAddon consumes it for types only — its handlers run on the host. Move '${decl.package}' from "dependencies" to "devDependencies".`);
|
|
552
565
|
}
|
|
553
566
|
else {
|
|
554
|
-
logger.critical(ErrorCode.REMOTE_ADDON_NOT_DEV_DEPENDENCY, `Remote addon '${namespace}' ('${decl.package}') is wired with wireRemoteAddon but is not in "devDependencies". Add '${decl.package}' to "devDependencies" (types only).`);
|
|
567
|
+
logger.critical(ErrorCode.REMOTE_ADDON_NOT_DEV_DEPENDENCY, `Remote addon '${namespace}' ('${decl.package}') is wired with wireRemoteAddon but is not in the "devDependencies" of ${pkgJsonPath}. Add '${decl.package}' to "devDependencies" (types only).`);
|
|
555
568
|
}
|
|
556
569
|
}
|
|
557
570
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/inspector",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.92",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/pikkujs/pikku.git",
|
|
@@ -54,10 +54,10 @@
|
|
|
54
54
|
"zod": "^4.4.3"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
|
-
"@pikku/core": "^0.12.
|
|
57
|
+
"@pikku/core": "^0.12.127"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"@pikku/core": "^0.12.
|
|
60
|
+
"@pikku/core": "^0.12.127",
|
|
61
61
|
"@types/node": "^24.13.3"
|
|
62
62
|
},
|
|
63
63
|
"engines": {
|