@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 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 `rootDir`, by any means the loader would use?
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 both
470
- * attempts means it is genuinely absent.
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, rootDir) => {
473
+ const isPackageResolvable = (pkg, dirs) => {
473
474
  const NOT_FOUND = new Set(['MODULE_NOT_FOUND', 'ERR_MODULE_NOT_FOUND']);
474
- let req;
475
- try {
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.resolve(specifier);
484
- return true;
478
+ req = createRequire(join(dir, 'package.json'));
479
+ }
480
+ catch {
481
+ return true; // cannot ask; do not accuse
485
482
  }
486
- catch (e) {
487
- if (!NOT_FOUND.has(e?.code))
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
- if (isPackageResolvable(decl.package, state.rootDir))
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. Install it, or remove the wireAddon call.`);
529
- }
530
- if (!Array.from(wireAddonDeclarations.values()).some((d) => d.remote))
531
- return;
532
- const pkgJsonPath = join(state.rootDir, 'package.json');
533
- if (!existsSync(pkgJsonPath))
534
- return; // no manifest to check (e.g. some tests)
535
- let pkgJson;
536
- try {
537
- pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
538
- }
539
- catch (e) {
540
- logger.warn(`Could not read ${pkgJsonPath} to verify remote addon dependencies: ${e?.message ?? e}`);
541
- return;
542
- }
543
- const prodDeps = pkgJson.dependencies ?? {};
544
- const devDeps = pkgJson.devDependencies ?? {};
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.91",
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.125"
57
+ "@pikku/core": "^0.12.127"
58
58
  },
59
59
  "devDependencies": {
60
- "@pikku/core": "^0.12.125",
60
+ "@pikku/core": "^0.12.127",
61
61
  "@types/node": "^24.13.3"
62
62
  },
63
63
  "engines": {