carrick 0.3.97 → 0.3.99

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 (58) hide show
  1. package/bin/carrick.mjs +19 -14
  2. package/dist/auth/run.d.ts +1 -1
  3. package/dist/auth/run.js +1 -1
  4. package/dist/contract.d.ts +15 -0
  5. package/dist/contract.js.map +1 -1
  6. package/dist/global-install.js +6 -5
  7. package/dist/global-install.js.map +1 -1
  8. package/dist/hook/reuse.d.ts +8 -3
  9. package/dist/hook/reuse.js +15 -3
  10. package/dist/hook/reuse.js.map +1 -1
  11. package/dist/init/connect.js +1 -1
  12. package/dist/init/connect.js.map +1 -1
  13. package/dist/init/doctor.js +5 -2
  14. package/dist/init/doctor.js.map +1 -1
  15. package/dist/init/hosted.d.ts +18 -1
  16. package/dist/init/hosted.js +84 -16
  17. package/dist/init/hosted.js.map +1 -1
  18. package/dist/init/install-id.d.ts +2 -2
  19. package/dist/init/install-id.js +3 -3
  20. package/dist/init/install-id.js.map +1 -1
  21. package/dist/init/mcp.d.ts +2 -2
  22. package/dist/init/mcp.js +2 -2
  23. package/dist/init/mcp.js.map +1 -1
  24. package/dist/init/outdated.d.ts +1 -1
  25. package/dist/init/outdated.js +1 -1
  26. package/dist/init/outdated.js.map +1 -1
  27. package/dist/init/output.d.ts +18 -1
  28. package/dist/init/output.js +40 -1
  29. package/dist/init/output.js.map +1 -1
  30. package/dist/init/remove.d.ts +71 -2
  31. package/dist/init/remove.js +474 -145
  32. package/dist/init/remove.js.map +1 -1
  33. package/dist/init/repo-copies.d.ts +16 -4
  34. package/dist/init/repo-copies.js +20 -6
  35. package/dist/init/repo-copies.js.map +1 -1
  36. package/dist/init/run.d.ts +12 -16
  37. package/dist/init/run.js +25 -42
  38. package/dist/init/run.js.map +1 -1
  39. package/dist/update.js +1 -0
  40. package/dist/update.js.map +1 -1
  41. package/package.json +6 -6
  42. package/plugin/.claude-plugin/plugin.json +1 -1
  43. package/sidecar/dist/src/bundler.d.ts +2 -0
  44. package/sidecar/dist/src/bundler.js +115 -39
  45. package/sidecar/dist/src/capture/index.d.ts +1 -0
  46. package/sidecar/dist/src/capture/index.js +91 -10
  47. package/sidecar/dist/src/capture/project-references.d.ts +67 -0
  48. package/sidecar/dist/src/capture/project-references.js +157 -0
  49. package/sidecar/dist/src/client-semantics.d.ts +36 -0
  50. package/sidecar/dist/src/client-semantics.js +1008 -0
  51. package/sidecar/dist/src/index.js +110 -10
  52. package/sidecar/dist/src/module-format.d.ts +32 -0
  53. package/sidecar/dist/src/module-format.js +102 -0
  54. package/sidecar/dist/src/project-loader.d.ts +24 -0
  55. package/sidecar/dist/src/project-loader.js +53 -5
  56. package/sidecar/dist/src/types.d.ts +86 -4
  57. package/sidecar/dist/src/validators.d.ts +463 -0
  58. package/sidecar/dist/src/validators.js +50 -0
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Which project types a file of a service whose tsconfig references others
3
+ * (carrick#1604).
4
+ *
5
+ * A "solution" tsconfig (`"files": []` plus `references`) carries no compiler
6
+ * options: they live in the projects it references. Parsing the named file
7
+ * alone typed every file with none, so `customConditions`, `paths` and the
8
+ * module resolution mode never applied and workspace imports fell through to
9
+ * unbuilt output.
10
+ *
11
+ * TypeScript's editor answers "which project types this file" by searching
12
+ * the named config, then its references depth-first in declared order, for
13
+ * the first project whose file list includes the file. That project is the
14
+ * file's owner here too, and a file is only ever typed under its owner's
15
+ * options: the loader builds one program per owner it is asked about, and
16
+ * the capture types each anchor in its owner's program. A file no reachable
17
+ * project includes is owned by the named config, which is how it was typed
18
+ * before references were read.
19
+ */
20
+ import * as path from 'node:path';
21
+ import ts from 'typescript';
22
+ /** Parse one config; throws on a file that cannot be read or parsed. */
23
+ function parseConfig(configPath, extendedConfigCache) {
24
+ const host = {
25
+ ...ts.sys,
26
+ onUnRecoverableConfigFileDiagnostic: (d) => {
27
+ throw new Error(ts.flattenDiagnosticMessageText(d.messageText, '\n'));
28
+ },
29
+ };
30
+ const parsed = ts.getParsedCommandLineOfConfigFile(configPath, {}, host, extendedConfigCache);
31
+ if (!parsed)
32
+ throw new Error(`failed to parse ${configPath}`);
33
+ return { configPath: path.resolve(configPath), parsed };
34
+ }
35
+ const fileKey = (file) => path.resolve(file);
36
+ /**
37
+ * The projects a named config reaches and the owner of each file among them.
38
+ * References are read only when a file the named config does not include is
39
+ * asked about, so a service whose files the named config includes costs one
40
+ * parse, as before.
41
+ */
42
+ export class ProjectGraph {
43
+ named;
44
+ /** References that could not be read, one line each. */
45
+ diagnostics = [];
46
+ extendedConfigCache = new Map();
47
+ walked;
48
+ namedFiles;
49
+ /** Throws when the named config cannot be read, as a bare parse would. */
50
+ constructor(configPath) {
51
+ this.named = parseConfig(configPath, this.extendedConfigCache);
52
+ this.namedFiles = new Set(this.named.parsed.fileNames.map(fileKey));
53
+ }
54
+ /** Whether the named config references other projects at all. */
55
+ get hasReferences() {
56
+ return (this.named.parsed.projectReferences?.length ?? 0) > 0;
57
+ }
58
+ /**
59
+ * The project that owns `file`: the first in search order whose file list
60
+ * includes it (the named config, then its references depth-first in
61
+ * declared order; a config reached twice is visited once). The named config
62
+ * when none does.
63
+ */
64
+ ownerOf(file) {
65
+ const key = fileKey(file);
66
+ if (this.namedFiles.has(key) || !this.hasReferences)
67
+ return this.named;
68
+ return this.projects().find((entry) => entry.files.has(key))?.project ?? this.named;
69
+ }
70
+ /** Position in search order, 0 for the named config. */
71
+ rank(project) {
72
+ if (project === this.named)
73
+ return 0;
74
+ const index = this.projects().findIndex((entry) => entry.project === project);
75
+ return index === -1 ? Number.MAX_SAFE_INTEGER : index;
76
+ }
77
+ projects() {
78
+ if (this.walked)
79
+ return this.walked;
80
+ const visited = new Set([this.named.configPath]);
81
+ const order = [];
82
+ const visit = (project, files) => {
83
+ order.push({ project, files });
84
+ for (const reference of project.parsed.projectReferences ?? []) {
85
+ const referencePath = path.resolve(ts.resolveProjectReferencePath(reference));
86
+ if (visited.has(referencePath))
87
+ continue;
88
+ visited.add(referencePath);
89
+ let child;
90
+ try {
91
+ child = parseConfig(referencePath, this.extendedConfigCache);
92
+ }
93
+ catch (err) {
94
+ this.diagnostics.push(`referenced tsconfig ${referencePath} (from ${project.configPath}) could not be read and was skipped: ` +
95
+ (err instanceof Error ? err.message : String(err)));
96
+ continue;
97
+ }
98
+ visit(child, new Set(child.parsed.fileNames.map(fileKey)));
99
+ }
100
+ };
101
+ visit(this.named, this.namedFiles);
102
+ this.walked = order;
103
+ return order;
104
+ }
105
+ }
106
+ /**
107
+ * For the loader: the config each file of the service is typed under. The
108
+ * returned function gives the owner's config path for a file, and the named
109
+ * config for no file; a reference that cannot be read goes to `report`. A named config that references nothing is not parsed
110
+ * here (`references` is never inherited through `extends`), so the function
111
+ * answers the named path for every file and the common case costs nothing.
112
+ */
113
+ export function serviceConfigPath(configPath, report = () => { }) {
114
+ const raw = ts.readConfigFile(configPath, ts.sys.readFile).config;
115
+ if (!Array.isArray(raw?.references) || raw.references.length === 0) {
116
+ return () => configPath;
117
+ }
118
+ let graph;
119
+ return (file) => {
120
+ if (file === undefined)
121
+ return configPath;
122
+ graph ??= new ProjectGraph(configPath);
123
+ const reported = graph.diagnostics.length;
124
+ const owner = graph.ownerOf(file);
125
+ graph.diagnostics.slice(reported).forEach(report);
126
+ return owner === graph.named ? configPath : owner.configPath;
127
+ };
128
+ }
129
+ /** Compiler options that only place output, so two projects differing only in these emit the same declarations. */
130
+ const OUTPUT_ONLY_OPTIONS = new Set([
131
+ 'rootDir',
132
+ 'outDir',
133
+ 'declarationDir',
134
+ 'outFile',
135
+ 'composite',
136
+ 'declaration',
137
+ 'declarationMap',
138
+ 'emitDeclarationOnly',
139
+ 'incremental',
140
+ 'tsBuildInfoFile',
141
+ 'configFilePath',
142
+ 'noEmit',
143
+ 'sourceMap',
144
+ 'inlineSourceMap',
145
+ 'inlineSources',
146
+ ]);
147
+ /**
148
+ * Whether two projects' options would emit a declaration the same way, so a
149
+ * type one of them names can be emitted by the other.
150
+ */
151
+ export function emitsAlike(a, b) {
152
+ const relevant = (options) => JSON.stringify(Object.keys(options)
153
+ .filter((key) => !OUTPUT_ONLY_OPTIONS.has(key))
154
+ .sort()
155
+ .map((key) => [key, options[key]]));
156
+ return relevant(a.parsed.options) === relevant(b.parsed.options);
157
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * `verify_client_semantics` (carrick#1564): check what a model said about an
3
+ * HTTP client library against the package's own type declarations.
4
+ *
5
+ * A claim names a member of a package export (or of an instance one of its
6
+ * factories returns) and says how that member is called: which option key
7
+ * carries the base URL, which HTTP method a verb sends, where the path, method
8
+ * and body sit. The scanner reads call sites through a claim only when this
9
+ * check verified it, and a verified claim becomes a fact that can fail a pull
10
+ * request check. So a claim is `verified` only when the declarations say so
11
+ * positively. `failed` means the declarations resolved and contradict the
12
+ * claim; `unchecked` means they could not be read, or say nothing at the place
13
+ * the claim needs (`any`, `unknown`, `{}`, an unconstrained type parameter).
14
+ * The scanner drops both.
15
+ *
16
+ * The export's type is read the way the service's own code would read it: a
17
+ * probe file in `from_dir` imports it, inside the service's program, under the
18
+ * service's compiler options. The declarations must be the installed
19
+ * package's: a `paths` alias or a `declare module` block the service writes
20
+ * for itself is not evidence about the library.
21
+ */
22
+ import { type Project } from 'ts-morph';
23
+ import type { SemanticsCheck, SemanticsModule, SemanticsResult } from './types.js';
24
+ export interface VerifyResult {
25
+ semantics: SemanticsResult[];
26
+ modules: SemanticsModule[];
27
+ }
28
+ export declare class ClientSemanticsVerifier {
29
+ private readonly project;
30
+ constructor(project: Project);
31
+ /**
32
+ * Judge every check, in request order, spending at most `budgetMs`. Checks
33
+ * the budget does not reach come back `unchecked` with reason `budget`.
34
+ */
35
+ run(fromDir: string, checks: SemanticsCheck[], budgetMs: number): VerifyResult;
36
+ }