@nifrajs/cli 2.14.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/check.d.ts.map +1 -1
  2. package/dist/check.js +27 -7
  3. package/dist/check.js.map +1 -1
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +40 -2
  6. package/dist/cli.js.map +1 -1
  7. package/dist/command-catalog.d.ts +6 -0
  8. package/dist/command-catalog.d.ts.map +1 -1
  9. package/dist/command-catalog.js +20 -2
  10. package/dist/command-catalog.js.map +1 -1
  11. package/dist/doctor.d.ts +42 -0
  12. package/dist/doctor.d.ts.map +1 -1
  13. package/dist/doctor.js +64 -7
  14. package/dist/doctor.js.map +1 -1
  15. package/dist/env-file.d.ts +17 -0
  16. package/dist/env-file.d.ts.map +1 -0
  17. package/dist/env-file.js +113 -0
  18. package/dist/env-file.js.map +1 -0
  19. package/dist/fix-recipes.d.ts.map +1 -1
  20. package/dist/fix-recipes.js +91 -15
  21. package/dist/fix-recipes.js.map +1 -1
  22. package/dist/internal/reserved-segment-codemod.d.ts +36 -0
  23. package/dist/internal/reserved-segment-codemod.d.ts.map +1 -0
  24. package/dist/internal/reserved-segment-codemod.js +134 -0
  25. package/dist/internal/reserved-segment-codemod.js.map +1 -0
  26. package/dist/internal/typescript-import.d.ts +1 -1
  27. package/dist/internal/typescript-import.d.ts.map +1 -1
  28. package/dist/internal/typescript-import.js +62 -11
  29. package/dist/internal/typescript-import.js.map +1 -1
  30. package/dist/introspect.d.ts +26 -1
  31. package/dist/introspect.d.ts.map +1 -1
  32. package/dist/introspect.js +74 -24
  33. package/dist/introspect.js.map +1 -1
  34. package/dist/mcp-http.js +1 -1
  35. package/dist/mcp-http.js.map +1 -1
  36. package/dist/mcp.d.ts +8 -0
  37. package/dist/mcp.d.ts.map +1 -1
  38. package/dist/mcp.js +68 -6
  39. package/dist/mcp.js.map +1 -1
  40. package/dist/recipes/3.0.0.d.ts +10 -0
  41. package/dist/recipes/3.0.0.d.ts.map +1 -0
  42. package/dist/recipes/3.0.0.js +23 -0
  43. package/dist/recipes/3.0.0.js.map +1 -0
  44. package/dist/recipes/index.d.ts.map +1 -1
  45. package/dist/recipes/index.js +2 -0
  46. package/dist/recipes/index.js.map +1 -1
  47. package/dist/release-verification.d.ts.map +1 -1
  48. package/dist/release-verification.js +15 -0
  49. package/dist/release-verification.js.map +1 -1
  50. package/dist/rules/routes.d.ts.map +1 -1
  51. package/dist/rules/routes.js +17 -30
  52. package/dist/rules/routes.js.map +1 -1
  53. package/dist/workspace-link.d.ts +37 -0
  54. package/dist/workspace-link.d.ts.map +1 -0
  55. package/dist/workspace-link.js +72 -0
  56. package/dist/workspace-link.js.map +1 -0
  57. package/docs/examples.json +1 -1
  58. package/docs/llms-full.txt +362 -20
  59. package/docs/types.json +933 -46
  60. package/package.json +9 -9
  61. package/src/check.ts +28 -7
  62. package/src/cli.ts +41 -2
  63. package/src/command-catalog.ts +22 -2
  64. package/src/doctor.ts +99 -8
  65. package/src/env-file.ts +121 -0
  66. package/src/fix-recipes.ts +101 -15
  67. package/src/internal/reserved-segment-codemod.ts +150 -0
  68. package/src/internal/typescript-import.ts +61 -11
  69. package/src/introspect.ts +82 -21
  70. package/src/mcp-http.ts +1 -1
  71. package/src/mcp.ts +81 -6
  72. package/src/recipes/3.0.0.ts +24 -0
  73. package/src/recipes/index.ts +2 -0
  74. package/src/release-verification.ts +17 -0
  75. package/src/rules/routes.ts +17 -30
  76. package/src/workspace-link.ts +80 -0
@@ -1,3 +1,4 @@
1
+ import { RESERVED_KEY_READOUT, reservedKeyFor } from "@nifrajs/client";
1
2
  import { diagnostic } from "../diagnostics.js";
2
3
  import { commentBlockHasMarker } from "./comment-markers.js";
3
4
  /**
@@ -5,26 +6,18 @@ import { commentBlockHasMarker } from "./comment-markers.js";
5
6
  * once before the registry runs - no rule re-walks source).
6
7
  *
7
8
  * NF-C018 exists because the typed client's proxy intercepts a fixed set of property names before
8
- * path resolution (`resolveSegment` + the thenable guard in @nifrajs/client): the seven HTTP verbs
9
- * (case-insensitively), `subscribe`, `ws`, `index`, and `then`. A route whose path contains a
10
- * static segment spelling one of these cannot be reached by DOT ACCESS - `api.delete.post` resolves
11
- * the DELETE verb, not the path. The typed spelling is a call on the parent node
12
- * (`api.api("delete").post()`, treaty.ts `CollisionEscape`), and the client type rejects the dot
13
- * access with the same guidance - so the collision is a warning that teaches the escape spelling,
14
- * not a blocking error: the route IS reachable, just not by the spelling its name suggests.
9
+ * path resolution (`resolveSegment` + the thenable guard in @nifrajs/client). A route whose path
10
+ * contains a static segment spelling one of these cannot be reached by DOT ACCESS -
11
+ * `api.delete.post` resolves the DELETE verb, not the path. The typed spelling is a call on the
12
+ * parent node (`api.api("delete").post()`, treaty.ts `SegmentCall`), and the client type rejects
13
+ * the dot access with the same guidance - so the collision is a warning that teaches the call
14
+ * spelling, not a blocking error: the route IS reachable, just not by the spelling its name
15
+ * suggests.
16
+ *
17
+ * The set itself is NOT copied here. It comes from `@nifrajs/client`'s `reserved.ts`, which is the
18
+ * one place it is written down and the one place the freeze policy lives - a lint that carried its
19
+ * own copy is a lint that can disagree with the compiler it is explaining.
15
20
  */
16
- const RESERVED_VERB_SEGMENTS = new Set([
17
- "get",
18
- "post",
19
- "put",
20
- "patch",
21
- "delete",
22
- "head",
23
- "options",
24
- ]);
25
- // Intercepted by exact match only (`key === "subscribe"` etc.), unlike the lowercased verbs.
26
- const RESERVED_EXACT_SEGMENTS = new Set(["subscribe", "ws", "index", "then"]);
27
- const RESERVED_READOUT = "get/post/put/patch/delete/head/options (any casing), subscribe, ws, index, then";
28
21
  /** Opt-out pragma for a route deliberately served only to NON-typed-client consumers. */
29
22
  const RESERVED_SEGMENT_PRAGMA = "nifra-expect reserved-segment";
30
23
  /** Parse-don't-cast over the project fact: a malformed entry is dropped, never trusted. */
@@ -54,15 +47,6 @@ function routeEscapeHint(path, colliding) {
54
47
  const before = segments.slice(0, Math.max(segments.indexOf(colliding), 0));
55
48
  return `${["api", ...before].join(".")}("${colliding}")`;
56
49
  }
57
- /** The reserved key a static path segment collides with, or undefined. Params/wildcards never collide. */
58
- function reservedCollision(segment) {
59
- if (segment.startsWith(":") || segment.startsWith("*"))
60
- return undefined;
61
- if (RESERVED_EXACT_SEGMENTS.has(segment))
62
- return segment;
63
- const lower = segment.toLowerCase();
64
- return RESERVED_VERB_SEGMENTS.has(lower) ? lower : undefined;
65
- }
66
50
  export const reservedSegmentRule = {
67
51
  code: "NF-C018",
68
52
  title: "Route segment collides with a reserved client proxy key",
@@ -73,7 +57,7 @@ export const reservedSegmentRule = {
73
57
  for (const segment of route.path.split("/")) {
74
58
  if (segment === "")
75
59
  continue;
76
- const collision = reservedCollision(segment);
60
+ const collision = reservedKeyFor(segment);
77
61
  if (collision === undefined)
78
62
  continue;
79
63
  let lines = linesByFile.get(route.file);
@@ -88,8 +72,11 @@ export const reservedSegmentRule = {
88
72
  severity: "warn",
89
73
  file: route.file,
90
74
  line: route.line,
91
- message: `${route.method} ${route.path} - segment '${segment}' collides with the reserved client proxy key '${collision}' (reserved: ${RESERVED_READOUT}); dot access cannot reach it - call the parent node with the segment instead (\`${routeEscapeHint(route.path, segment)}\`), rename the segment, or mark an intentionally non-typed-client route with \`// ${RESERVED_SEGMENT_PRAGMA}\` above the registration`,
75
+ message: `${route.method} ${route.path} - segment '${segment}' collides with the reserved client proxy key '${collision}' (reserved: ${RESERVED_KEY_READOUT}); dot access cannot reach it - call the parent node with the segment instead (\`${routeEscapeHint(route.path, segment)}\`), rename the segment, or mark an intentionally non-typed-client route with \`// ${RESERVED_SEGMENT_PRAGMA}\` above the registration. \`nifra fix --code NF-C018\` rewrites the broken call sites for you`,
92
76
  evidence: [`${route.method} ${route.path}`, `segment: ${segment}`],
77
+ // The route is fine; what needs editing is every typed-client call site the collision
78
+ // broke, which the recipe finds from the compiler rather than from this one finding.
79
+ fix: { recipe: "client.reserved-segment", command: "nifra fix --code NF-C018" },
93
80
  verify: "nifra check --lints-only",
94
81
  }));
95
82
  break; // one finding per route, even if several segments collide
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/rules/routes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/rules/routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACtE,OAAO,EAAmB,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAG5D;;;;;;;;;;;;;;;;GAgBG;AAEH,yFAAyF;AACzF,MAAM,uBAAuB,GAAG,+BAA+B,CAAA;AAS/D,2FAA2F;AAC3F,SAAS,UAAU,CAAC,GAAgB;IAClC,MAAM,GAAG,GAAuB,GAAG,CAAC,OAAO,CAAC,MAAM,CAAA;IAClD,MAAM,GAAG,GAAsB,EAAE,CAAA;IACjC,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;QACvB,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;YAAE,SAAQ;QAC9E,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;QACvD,IACE,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ;YAC/B,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ;YAC/B,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ;YACjC,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ;YAE/B,SAAQ;QACV,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IAC9F,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED;;;;GAIG;AACH,SAAS,eAAe,CAAC,IAAY,EAAE,SAAiB;IACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,EAAE,CAAC,CAAA;IACpE,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IAC1E,OAAO,GAAG,CAAC,KAAK,EAAE,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,SAAS,IAAI,CAAA;AAC1D,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAc;IAC5C,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,yDAAyD;IAChE,KAAK,CAAC,IAAI,CAAC,GAAG;QACZ,MAAM,QAAQ,GAAiB,EAAE,CAAA;QACjC,MAAM,WAAW,GAAG,IAAI,GAAG,EAA6B,CAAA;QACxD,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACpC,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC5C,IAAI,OAAO,KAAK,EAAE;oBAAE,SAAQ;gBAC5B,MAAM,SAAS,GAAG,cAAc,CAAC,OAAO,CAAC,CAAA;gBACzC,IAAI,SAAS,KAAK,SAAS;oBAAE,SAAQ;gBACrC,IAAI,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;gBACvC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;oBACxB,KAAK,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;oBAC/D,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;gBACpC,CAAC;gBACD,IAAI,qBAAqB,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,uBAAuB,CAAC;oBAAE,SAAQ;gBAC/E,QAAQ,CAAC,IAAI,CACX,UAAU,CAAC;oBACT,IAAI,EAAE,SAAS;oBACf,QAAQ,EAAE,MAAM;oBAChB,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,OAAO,EAAE,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,eAAe,OAAO,kDAAkD,SAAS,gBAAgB,oBAAoB,oFAAoF,eAAe,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,sFAAsF,uBAAuB,gGAAgG;oBAChe,QAAQ,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,YAAY,OAAO,EAAE,CAAC;oBAClE,sFAAsF;oBACtF,qFAAqF;oBACrF,GAAG,EAAE,EAAE,MAAM,EAAE,yBAAyB,EAAE,OAAO,EAAE,0BAA0B,EAAE;oBAC/E,MAAM,EAAE,0BAA0B;iBACnC,CAAC,CACH,CAAA;gBACD,MAAK,CAAC,0DAA0D;YAClE,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF,CAAA;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAc;IAC3C,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,8BAA8B;IACrC,KAAK,CAAC,IAAI,CAAC,GAAG;QACZ,MAAM,QAAQ,GAAiB,EAAE,CAAA;QACjC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAwC,CAAA;QAC9D,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACpC,IAAI,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YACjC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,IAAI,GAAG,IAAI,GAAG,EAAE,CAAA;gBAChB,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;YAC9B,CAAC;YACD,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,CAAA;YAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YAC3B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;gBACpB,SAAQ;YACV,CAAC;YACD,QAAQ,CAAC,IAAI,CACX,UAAU,CAAC;gBACT,IAAI,EAAE,SAAS;gBACf,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,OAAO,EAAE,GAAG,GAAG,oDAAoD,KAAK,CAAC,IAAI,kEAAkE;gBAC/I,QAAQ,EAAE,CAAC,GAAG,EAAE,4BAA4B,KAAK,CAAC,IAAI,EAAE,CAAC;gBACzD,MAAM,EAAE,0BAA0B;aACnC,CAAC,CACH,CAAA;QACH,CAAC;QACD,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF,CAAA;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,mBAAmB,EAAE,kBAAkB,CAAC,CAAC,CAAA"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Resolving a WORKSPACE-LINKED dependency back to the directory it really lives in.
3
+ *
4
+ * A workspace link is the one install whose realpath deliberately leaves the project: `link:` and
5
+ * `workspaces` entries point at a source checkout, routinely in a sibling repository. That is exactly
6
+ * what makes such a package able to go stale (its `dist/` is nobody's build step) and exactly what a
7
+ * project-contained path check rejects - so the rebuild has to resolve the escape on purpose, under
8
+ * its own rules, rather than reuse `resolveInsideProject`.
9
+ *
10
+ * The rules that keep it safe:
11
+ * - the path is never taken from a diagnostic; it is re-derived from the package NAME by walking the
12
+ * project's own `node_modules` chain, so only a symlink the package manager already created can be
13
+ * reached, and only a package the project actually declares;
14
+ * - a copy that resolves INSIDE `node_modules` is a registry tarball - immutable, never stale, and
15
+ * never rebuilt;
16
+ * - the package must declare the build script itself. nifra runs the project's own script, never a
17
+ * command of its own composition, and never through a shell.
18
+ */
19
+ export declare function isPackageName(value: string): boolean;
20
+ /** The package's own build script name, or `undefined` when it declares none. */
21
+ export declare function buildScriptName(meta: Record<string, unknown> | undefined): string | undefined;
22
+ export type WorkspaceLinkResolution = {
23
+ readonly ok: true;
24
+ readonly dir: string;
25
+ readonly buildScript: string;
26
+ } | {
27
+ readonly ok: false;
28
+ readonly reason: string;
29
+ };
30
+ /**
31
+ * Resolve `name` to the workspace-linked checkout the project loads it from, refusing with a stated
32
+ * reason rather than resolving to nothing. The upward walk is unbounded on purpose: it has to reach the
33
+ * same hoisted copy the staleness scan found, which in a monorepo sits above the package being checked.
34
+ * It only ever inspects `node_modules/<name>` directories, so it cannot wander into an arbitrary path.
35
+ */
36
+ export declare function resolveWorkspaceLinkedPackage(cwd: string, name: string): Promise<WorkspaceLinkResolution>;
37
+ //# sourceMappingURL=workspace-link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-link.d.ts","sourceRoot":"","sources":["../src/workspace-link.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAaH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,iFAAiF;AACjF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAK7F;AAED,MAAM,MAAM,uBAAuB,GAC/B;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACzE;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAEnD;;;;;GAKG;AACH,wBAAsB,6BAA6B,CACjD,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,uBAAuB,CAAC,CAwBlC"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Resolving a WORKSPACE-LINKED dependency back to the directory it really lives in.
3
+ *
4
+ * A workspace link is the one install whose realpath deliberately leaves the project: `link:` and
5
+ * `workspaces` entries point at a source checkout, routinely in a sibling repository. That is exactly
6
+ * what makes such a package able to go stale (its `dist/` is nobody's build step) and exactly what a
7
+ * project-contained path check rejects - so the rebuild has to resolve the escape on purpose, under
8
+ * its own rules, rather than reuse `resolveInsideProject`.
9
+ *
10
+ * The rules that keep it safe:
11
+ * - the path is never taken from a diagnostic; it is re-derived from the package NAME by walking the
12
+ * project's own `node_modules` chain, so only a symlink the package manager already created can be
13
+ * reached, and only a package the project actually declares;
14
+ * - a copy that resolves INSIDE `node_modules` is a registry tarball - immutable, never stale, and
15
+ * never rebuilt;
16
+ * - the package must declare the build script itself. nifra runs the project's own script, never a
17
+ * command of its own composition, and never through a shell.
18
+ */
19
+ import { sep } from "node:path";
20
+ import { resolvedInstalledCopy } from "@nifrajs/web/internal/parity";
21
+ /** The one script name a rebuild will run. Guessing among `prepare`/`compile`/`build:*` would risk
22
+ * running something that is not the build at all; a package that names it differently is reported as
23
+ * un-automatable instead. */
24
+ const BUILD_SCRIPT = "build";
25
+ /** A package name npm would accept, with no path segment that could climb out of `node_modules`. */
26
+ const PACKAGE_NAME = /^@?[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)?$/;
27
+ export function isPackageName(value) {
28
+ return PACKAGE_NAME.test(value) && !value.split("/").some((part) => part === "." || part === "..");
29
+ }
30
+ /** The package's own build script name, or `undefined` when it declares none. */
31
+ export function buildScriptName(meta) {
32
+ const scripts = meta?.scripts;
33
+ if (typeof scripts !== "object" || scripts === null)
34
+ return undefined;
35
+ const script = scripts[BUILD_SCRIPT];
36
+ return typeof script === "string" && script.trim().length > 0 ? BUILD_SCRIPT : undefined;
37
+ }
38
+ /**
39
+ * Resolve `name` to the workspace-linked checkout the project loads it from, refusing with a stated
40
+ * reason rather than resolving to nothing. The upward walk is unbounded on purpose: it has to reach the
41
+ * same hoisted copy the staleness scan found, which in a monorepo sits above the package being checked.
42
+ * It only ever inspects `node_modules/<name>` directories, so it cannot wander into an arbitrary path.
43
+ */
44
+ export async function resolveWorkspaceLinkedPackage(cwd, name) {
45
+ if (!isPackageName(name))
46
+ return { ok: false, reason: `not a package name: ${name}` };
47
+ // Boundary "" and the `node_modules` refusal below mirror `collectStaleWorkspaceDists` exactly, so
48
+ // the rebuild always lands on the same copy the staleness scan flagged.
49
+ const copy = await resolvedInstalledCopy(cwd, "", name);
50
+ if (copy === undefined)
51
+ return { ok: false, reason: `${name} is not installed under ${cwd}` };
52
+ if (copy.path.includes(`${sep}node_modules${sep}`)) {
53
+ return {
54
+ ok: false,
55
+ reason: `${name} resolves to a registry install (${copy.path}), which cannot be stale or rebuilt`,
56
+ };
57
+ }
58
+ const meta = (await Bun.file(`${copy.path}${sep}package.json`)
59
+ .json()
60
+ .catch(() => undefined));
61
+ if (meta === undefined)
62
+ return { ok: false, reason: `${copy.path} has no readable package.json` };
63
+ const buildScript = buildScriptName(meta);
64
+ if (buildScript === undefined) {
65
+ return {
66
+ ok: false,
67
+ reason: `${name} declares no "${BUILD_SCRIPT}" script in ${copy.path}/package.json, so its artifact cannot be rebuilt automatically - build it the way that package expects`,
68
+ };
69
+ }
70
+ return { ok: true, dir: copy.path, buildScript };
71
+ }
72
+ //# sourceMappingURL=workspace-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-link.js","sourceRoot":"","sources":["../src/workspace-link.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAC/B,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAA;AAEpE;;6BAE6B;AAC7B,MAAM,YAAY,GAAG,OAAO,CAAA;AAE5B,oGAAoG;AACpG,MAAM,YAAY,GAAG,2CAA2C,CAAA;AAEhE,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,IAAI,CAAC,CAAA;AACpG,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,eAAe,CAAC,IAAyC;IACvE,MAAM,OAAO,GAAG,IAAI,EAAE,OAAO,CAAA;IAC7B,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,SAAS,CAAA;IACrE,MAAM,MAAM,GAAI,OAAmC,CAAC,YAAY,CAAC,CAAA;IACjE,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,CAAA;AAC1F,CAAC;AAMD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,GAAW,EACX,IAAY;IAEZ,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,uBAAuB,IAAI,EAAE,EAAE,CAAA;IACrF,mGAAmG;IACnG,wEAAwE;IACxE,MAAM,IAAI,GAAG,MAAM,qBAAqB,CAAC,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,CAAA;IACvD,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,2BAA2B,GAAG,EAAE,EAAE,CAAA;IAC7F,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,GAAG,eAAe,GAAG,EAAE,CAAC,EAAE,CAAC;QACnD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,GAAG,IAAI,oCAAoC,IAAI,CAAC,IAAI,qCAAqC;SAClG,CAAA;IACH,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,GAAG,GAAG,cAAc,CAAC;SAC3D,IAAI,EAAE;SACN,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAwC,CAAA;IACjE,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,+BAA+B,EAAE,CAAA;IACjG,MAAM,WAAW,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;IACzC,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,GAAG,IAAI,iBAAiB,YAAY,eAAe,IAAI,CAAC,IAAI,wGAAwG;SAC7K,CAAA;IACH,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,CAAA;AAClD,CAAC"}
@@ -309,7 +309,7 @@
309
309
  "name": "PORTABLE_HOOK",
310
310
  "topic": "Nifra - Plugins & middleware",
311
311
  "slug": "plugins",
312
- "code": "import type { Middleware } from \"@nifrajs/core/server\"\n\n// ONE implementation, fast on every runtime: on Bun/Deno it mutates the\n// response's own Headers in place; on Node it writes the outcome record on the\n// direct socket writer - no Web Request/Response is ever built for it.\nexport function serverName(value: string): Middleware {\n return {\n onResponseHeaders(headers, req, status) {\n if (status < 500) headers.set(\"server\", value)\n },\n }\n}"
312
+ "code": "import { responseObserver } from \"@nifrajs/core/response-observer\"\nimport { server } from \"@nifrajs/core/server\"\nimport type { Middleware } from \"@nifrajs/core/server\"\n\n// ONE implementation, fast on every runtime: on Bun/Deno it mutates the\n// response's own Headers in place; on Node it writes the outcome record on the\n// direct socket writer - no Web Request/Response is ever built for it.\nexport function serverName(value: string): Middleware {\n return {\n onResponseHeaders(headers, req, status) {\n if (status < 500) headers.set(\"server\", value)\n },\n }\n}\n\nconst app = server().use(responseObserver()).use(serverName(\"nifra\"))"
313
313
  },
314
314
  {
315
315
  "name": "RELIABILITY",